pax_global_header00006660000000000000000000000064145371337540014526gustar00rootroot0000000000000052 comment=edf15a7ee8b1839c593a6bea80d7bedd14d92830 jpype-1.5.0/000077500000000000000000000000001453713375400126605ustar00rootroot00000000000000jpype-1.5.0/.azure/000077500000000000000000000000001453713375400140645ustar00rootroot00000000000000jpype-1.5.0/.azure/build.yml000066400000000000000000000042461453713375400157140ustar00rootroot00000000000000# JPype CI pipeline trigger: branches: include: - master - releases/* paths: include: - .azure/build.yml - doc/* - setup.py - setupext/* - jpype/* - native/* - test/* variables: # indicate whether the testsuite should skip long running tests or not. - name: jpypetest.fast value: 'false' jobs: - job: Deps pool: vmImage: "ubuntu-latest" steps: - template: scripts/ivy.yml - job: Documentation pool: vmImage: "ubuntu-latest" steps: - template: scripts/documentation.yml - job: Coverage pool: vmImage: "ubuntu-latest" dependsOn: Deps steps: - template: scripts/deps.yml - template: scripts/coverage.yml - job: Tracing pool: vmImage: "ubuntu-latest" steps: - template: scripts/tracing.yml - job: Test dependsOn: Deps strategy: matrix: linux-3.8: imageName: "ubuntu-latest" python.version: '3.8' linux-3.9: imageName: "ubuntu-latest" python.version: '3.9' linux-3.10: imageName: "ubuntu-latest" python.version: '3.10' linux-3.11: imageName: "ubuntu-latest" python.version: '3.11' linux-3.12: imageName: "ubuntu-latest" python.version: '3.12' windows-3.8: imageName: "windows-2019" python.version: '3.8' windows-3.9: imageName: "windows-2019" python.version: '3.9' #jpypetest.fast: 'true' windows-3.10: imageName: "windows-2019" python.version: '3.10' windows-3.11: imageName: "windows-2019" python.version: '3.11' windows-3.12: imageName: "windows-2019" python.version: '3.12' mac-3.9: imageName: "macos-11" python.version: '3.9' jpypetest.fast: 'true' pool: vmImage: $(imageName) steps: - template: scripts/deps.yml - template: scripts/test.yml - job: Debug condition: eq(1,0) dependsOn: Deps strategy: matrix: linux-3.8: imageName: "ubuntu-16.04" jdk_version: "1.11" python.version: '3.8' pool: vmImage: $(imageName) steps: - template: scripts/deps.yml - template: scripts/debug.yml jpype-1.5.0/.azure/doc-requirements.txt000066400000000000000000000002311453713375400201070ustar00rootroot00000000000000 # TODO: consider unpinning these? Pygments==2.15.0 docutils==0.14 commonmark==0.8.1 recommonmark==0.5.0 sphinx sphinx-rtd-theme readthedocs-sphinx-ext jpype-1.5.0/.azure/release.yml000066400000000000000000000055071453713375400162360ustar00rootroot00000000000000# JPype Release pipeline trigger: none pr: branches: include: - releases/* paths: include: - .bumpversion.cfg - .azure/release.yml variables: package_name: JPype1 stages: - stage: Initial jobs: - job: SourceDistribution pool: vmImage: "ubuntu-latest" steps: - template: scripts/sdist.yml - template: scripts/ivy.yml - stage: Package jobs: # From https://iscinumpy.gitlab.io/post/azure-devops-python-wheels/ - job: ManyLinux condition: eq(1,1) strategy: matrix: # Disabled because it is fetching beta images # 64Bit2014: # arch: aarch64 # plat: manylinux2014_aarch64 # image: quay.io/pypa/manylinux2014_aarch64 # python.architecture: aarch64 64Bit: arch: x86_64 plat: manylinux2014_x86_64 image: quay.io/pypa/manylinux2014_x86_64 python.architecture: x64 32Bit: arch: i686 plat: manylinux2014_i686 image: quay.io/pypa/manylinux2014_i686 python.architecture: x86 pool: vmImage: "ubuntu-latest" steps: - template: scripts/deps.yml - template: scripts/wheels-linux.yml - template: scripts/publish-dist.yml - job: Windows_x64 condition: eq(1,1) strategy: matrix: Python38: python.version: '3.8' python.architecture: 'x64' Python39: python.version: '3.9' python.architecture: 'x64' Python310: python.version: '3.10' python.architecture: 'x64' Python311: python.version: '3.11' python.architecture: 'x64' Python312: python.version: '3.12' python.architecture: 'x64' pool: vmImage: "windows-2019" steps: - template: scripts/deps.yml - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' architecture: '$(python.architecture)' - template: scripts/jdk.yml parameters: version: '8' - template: scripts/wheels.yml - template: scripts/publish-dist.yml - job: OSX condition: eq(1,1) variables: python.architecture: 'x64' strategy: matrix: Python38: python.version: '3.8' Python39: python.version: '3.9' Python310: python.version: '3.10' Python311: python.version: '3.11' Python312: python.version: '3.12' pool: vmImage: "macos-11" steps: - template: scripts/deps.yml - script: .azure/scripts/osx-python.sh '$(python.version)' displayName: Install Python.org Python - template: scripts/jdk.yml parameters: version: '8' - template: scripts/wheels.yml - template: scripts/publish-dist.yml jpype-1.5.0/.azure/scripts/000077500000000000000000000000001453713375400155535ustar00rootroot00000000000000jpype-1.5.0/.azure/scripts/build-wheels.sh000077500000000000000000000017011453713375400204750ustar00rootroot00000000000000#!/bin/bash set -e -x # Collect the pythons pys=(/opt/python/cp*/bin) # Exclude specific Pythons (3.6) pys=(${pys[@]//*36*/}) # Compile wheels for PYBIN in "${pys[@]}"; do echo "Compile $PYBIN" ls -l /io/dist "${PYBIN}/pip" install -r /io/dev-requirements.txt "${PYBIN}/pip" wheel /io/dist/$package_name-*.tar.gz -w wheelhouse/ -v done echo "==============" # Bundle external shared libraries into the wheels for whl in wheelhouse/$package_name-*.whl; do echo "Audit $whl" auditwheel repair --plat $PLAT "$whl" -w /io/wheelhouse/ done echo "==============" # Install packages and test for PYBIN in "${pys[@]}"; do echo "Test install $PYBIN $package_name" "${PYBIN}/python" -m pip install $package_name --no-index -f /io/wheelhouse # Manylinux does not have a JVM so there is no way to test the wheel in the docker # "${PYBIN}/pip" install -r /io/test-requirements.txt # "${PYBIN}/pytest" /io/test/jpypetest done jpype-1.5.0/.azure/scripts/coverage.yml000066400000000000000000000026771453713375400201050ustar00rootroot00000000000000# This task produces the coverage reports and publishes them to codecov. steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - template: jdk.yml parameters: version: 11 - script: | python setup.py test_java pip install gcovr pytest-cov jedi pip install -r test-requirements.txt pip install numpy typing_extensions displayName: 'Install requirements' - script: | python setup.py --enable-coverage --enable-build-jar build_ext --inplace displayName: 'Build' - script: | python -m pytest -v -s test/jpypetest --cov=jpype --cov-report=xml:coverage_py.xml --classpath="build/classes" --jacoco --checkjni displayName: 'Test' - script: | gcovr -r . --xml -o coverage.xml --exclude-unreachable-branches --exclude-throw-branches java -jar lib/org.jacoco.cli-0.8.5-nodeps.jar report build/coverage/jacoco.exec --classfiles build/classes/ --xml coverage_java.xml --sourcefiles native/java bash <(curl -s https://codecov.io/bash) -f coverage.xml -f coverage_py.xml -f coverage_java.xml -X gcov displayName: 'Report' - task: PublishCodeCoverageResults@1 inputs: codeCoverageTool: 'JaCoCo' summaryFileLocation: coverage_java.xml pathToSources: native/java - task: PublishCodeCoverageResults@1 inputs: codeCoverageTool: 'cobertura' summaryFileLocation: coverage.xml - task: PublishCodeCoverageResults@1 inputs: codeCoverageTool: 'cobertura' summaryFileLocation: coverage_py.xml jpype-1.5.0/.azure/scripts/debug.yml000066400000000000000000000011751453713375400173700ustar00rootroot00000000000000# This task is used when there is an error in the CI that can't be # replicated locally. steps: - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' - script: | sudo apt install gdb python setup.py sdist pip install dist/* displayName: 'Build module' - script: python -c "import jpype" displayName: 'Check module' - script: | python setup.py test_java pip install -r test-requirements.txt pip install numpy jedi gdb -ex 'handle SIGSEGV nostop noprint pass' -ex "run -m pytest -v test/jpypetest/test_jstring.py --checkjni" -ex "bt" -ex "quit" python displayName: 'Debug module' jpype-1.5.0/.azure/scripts/deps.yml000066400000000000000000000001631453713375400172310ustar00rootroot00000000000000steps: - task: DownloadPipelineArtifact@2 inputs: source: current artifact: artifact_Deps path: lib jpype-1.5.0/.azure/scripts/documentation.yml000066400000000000000000000012641453713375400211520ustar00rootroot00000000000000# This task tries to verify if the documentation will build properly on RTD steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | pip install --exists-action=w --no-cache-dir -r test-requirements.txt -r .azure/doc-requirements.txt displayName: 'Install requirements' - script: | python -m sphinx -T -b readthedocs -d _build/doctrees-readthedocs -D language=en doc build/html displayName: 'Check documentation' - task: CopyFiles@2 inputs: contents: 'build/html/**' targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishBuildArtifacts@1 inputs: pathToPublish: $(Build.ArtifactStagingDirectory) artifactName: documentation jpype-1.5.0/.azure/scripts/ivy.yml000066400000000000000000000005551453713375400171120ustar00rootroot00000000000000steps: - script: | wget "https://repo1.maven.org/maven2/org/apache/ivy/ivy/2.4.0/ivy-2.4.0.jar" -P lib java -jar lib/ivy-2.4.0.jar -ivy ivy.xml -retrieve 'lib/[artifact]-[revision](-[classifier]).[ext]' displayName: 'Resolve' - task: PublishPipelineArtifact@0 inputs: artifactName: 'artifact_Deps' targetPath: 'lib' displayName: Publish deps jpype-1.5.0/.azure/scripts/jdk.yml000066400000000000000000000006171453713375400170520ustar00rootroot00000000000000parameters: - name: version type: string default: 8 steps: - script: | set v="##vso[task.setvariable variable=JAVA_HOME]%JAVA_HOME_${{parameters.version}}_X64%" echo %v:"=% condition: eq(variables['Agent.OS'], 'Windows_NT') - script: | echo "##vso[task.setvariable variable=JAVA_HOME]$(JAVA_HOME_${{parameters.version}}_X64)" condition: ne(variables['Agent.OS'], 'Windows_NT') jpype-1.5.0/.azure/scripts/osx-python.sh000077500000000000000000000017211453713375400202430ustar00rootroot00000000000000#!/usr/bin/env bash PYTHON_VERSION="$1" case $PYTHON_VERSION in 3.7) FULL_VERSION=3.7.9 INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.8) FULL_VERSION=3.8.10 INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.9) FULL_VERSION=3.9.12 INSTALLER_NAME=python-$FULL_VERSION-macosx10.9.pkg ;; 3.10) FULL_VERSION=3.10.11 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; 3.11) FULL_VERSION=3.11.7 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg ;; 3.12) FULL_VERSION=3.12.0 INSTALLER_NAME=python-$FULL_VERSION-macos11.pkg esac URL=https://www.python.org/ftp/python/$FULL_VERSION/$INSTALLER_NAME PY_PREFIX=/Library/Frameworks/Python.framework/Versions set -e -x curl $URL > $INSTALLER_NAME sudo installer -pkg $INSTALLER_NAME -target / sudo rm -f /usr/local/bin/python sudo ln -s /usr/local/bin/python$PYTHON_VERSION /usr/local/bin/python which python python --version python -m ensurepip python -m pip install setuptools wheel jpype-1.5.0/.azure/scripts/publish-dist.yml000066400000000000000000000002331453713375400207030ustar00rootroot00000000000000steps: - task: PublishPipelineArtifact@0 inputs: artifactName: 'artifact_$(Agent.JobName)_$(Agent.OS)_$(python.architecture)' targetPath: 'dist' jpype-1.5.0/.azure/scripts/sdist.yml000066400000000000000000000004711453713375400174260ustar00rootroot00000000000000steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | python -m pip install setuptools python setup.py sdist displayName: Build sdist - task: PublishPipelineArtifact@0 inputs: artifactName: 'artifact_SourceDistribution' targetPath: 'dist' displayName: Publish sdist jpype-1.5.0/.azure/scripts/test.yml000066400000000000000000000025321453713375400172570ustar00rootroot00000000000000# This task tests individual platforms and versions steps: - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' - template: jdk.yml parameters: version: 11 - script: | python -m pip install --upgrade pytest setuptools python setup.py build_ext --inplace displayName: 'Build module' - script: | pip install numpy jedi typing_extensions python -c "import jpype" displayName: 'Check module' - script: | python setup.py test_java pip install -r test-requirements.txt displayName: 'Install test' - script: | python -m pytest -v --junit-xml=build/test/test.xml test/jpypetest --checkjni displayName: 'Test JDK 11' condition: eq(variables['jpypetest.fast'], 'false') - script: | python -m pytest -v --junit-xml=build/test/test.xml test/jpypetest --checkjni --fast displayName: 'Test JDK 11 (fast)' condition: eq(variables['jpypetest.fast'], 'true') # presence of jpype/ seems to confuse entry_points so `cd` elsewhere - script: | pip install . mkdir empty cd empty python -m PyInstaller.utils.run_tests --include_only jpype._pyinstaller. displayName: 'Test PyInstaller result' - task: PublishTestResults@2 condition: succeededOrFailed() inputs: testResultsFiles: 'build/test/test.xml' testRunTitle: 'Publish test results for Python $(python.version) with JDK 11' jpype-1.5.0/.azure/scripts/tracing.yml000066400000000000000000000003651453713375400177310ustar00rootroot00000000000000# This job verifies that nothing is broken in the tracing compile steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | python setup.py --enable-tracing --enable-build-jar build_ext --inplace displayName: 'Build' jpype-1.5.0/.azure/scripts/wheels-linux.yml000066400000000000000000000014701453713375400207240ustar00rootroot00000000000000steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - task: DownloadPipelineArtifact@2 inputs: source: current artifact: artifact_SourceDistribution path: dist - script: | sudo apt-get update sudo apt-get install -y qemu qemu-user-static qemu-user binfmt-support sudo docker run --rm --privileged hypriot/qemu-register condition: and(succeeded(), eq(variables['arch'], 'aarch64')) displayName: 'Install QEMU' - script: | ls -l ls -l dist displayName: Sanity check - script: | set -ex docker run -e PLAT=$(plat) -e package_name=$(package_name) --rm -v `pwd`:/io $(image) /io/.azure/scripts/build-wheels.sh displayName: Build wheels - script: | ls -lh wheelhouse/ rm dist/* cp wheelhouse/$(package_name)*.whl dist/. displayName: Copy wheels jpype-1.5.0/.azure/scripts/wheels.yml000066400000000000000000000014711453713375400175700ustar00rootroot00000000000000# This job creates wheels for Windows/OSX steps: - script: | mkdir -p dist python -m pip install --upgrade pip python -m pip install --upgrade wheel setuptools displayName: 'Install dependencies' - script: | python -m pip wheel . -w wheelhouse/ displayName: 'Build wheel' - script: | ls -lh wheelhouse cp wheelhouse/$(package_name)* dist/. displayName: 'Show wheelhouse' - script: | python -m pip install JPype1 --no-index -f wheelhouse displayName: 'Install module' - script: | python setup.py test_java python -m pip install -r test-requirements.txt displayName: 'Install test' - script: | ls -l ls lib/ displayName: 'Check deps' - script: | python -m pytest -v --junit-xml=build/test/test.xml test/jpypetest --checkjni --fast displayName: 'Test module' jpype-1.5.0/.bumpversion.cfg000066400000000000000000000012201453713375400157630ustar00rootroot00000000000000[bumpversion] current_version = 1.5.0 commit = True tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\_(?P[a-z]+)(?P\d+))? serialize = {major}.{minor}.{patch}_{release}{build} {major}.{minor}.{patch} [bumpversion:part:release] first_value = dev optional_value = prod values = dev prod [bumpversion:part:build] [bumpversion:file:setup.py] [bumpversion:file:native/python/pyjp_module.cpp] [bumpversion:file:jpype/__init__.py] [bumpversion:file:native/java/org/jpype/JPypeContext.java] [bumpversion:file:doc/CHANGELOG.rst] search = Latest Changes: replace = Latest Changes: - **{new_version} - {now:%Y-%m-%d}** jpype-1.5.0/.gitattributes000066400000000000000000000007661453713375400155640ustar00rootroot00000000000000* test=auto # Specify lf for all source files *.py text eol=lf *.cpp text eol=lf *.c text eol=lf *.h text eol=lf *.java text eol=lf *.rst text eol=lf *.sh text eol=lf *.cfg text eol=lf *.yml text eol=lf *.xsl text eol=lf *.css text eol=lf *.ps1 text eol=crlf *.cmd text eol=crlf *.bat text eol=crlf *.properties text eol=crlf README text eol=lf LICENSE text eol=lf Makefile text eol=lf Manifest.in text eol=lf *.xml text eol=lf *.md text *.txt text .gitignore text eol=lf .gitattributes text eol=lf jpype-1.5.0/.github/000077500000000000000000000000001453713375400142205ustar00rootroot00000000000000jpype-1.5.0/.github/workflows/000077500000000000000000000000001453713375400162555ustar00rootroot00000000000000jpype-1.5.0/.github/workflows/codeql.yml000066400000000000000000000021241453713375400202460ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "master" ] pull_request: branches: [ "master" ] schedule: - cron: "27 1 * * 1" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ java, cpp, python ] steps: - name: Checkout uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v2 if: ${{ matrix.language == 'python' }} - name: Build cpp run: python3 setup.py build if: ${{ matrix.language == 'cpp' }} - name: Build java run: ant -f native/build.xml if: ${{ matrix.language == 'java' }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{ matrix.language }}" jpype-1.5.0/.github/workflows/mypy.yml000066400000000000000000000006541453713375400200030ustar00rootroot00000000000000name: Mypy on: [push, pull_request] jobs: build: runs-on: ubuntu-latest name: mypy steps: - uses: actions/checkout@v1 - name: Set up Python 3.11 uses: actions/setup-python@v1 with: python-version: 3.11 - name: Install Dependencies run: | pip install mypy numpy types-pyinstaller pytest packaging - name: mypy run: | mypy ./jpype/ ./test/jpypetest/ jpype-1.5.0/.gitignore000077500000000000000000000025301453713375400146530ustar00rootroot00000000000000MANIFEST Makefile* *~ /test/classes/ /test/jars/ **/test.pic build/ .buildozer bin native/*.jar org.jpype.jar # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so *.dll # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage jacoco.exec .cache nosetests.xml coverage*.xml # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ /project/jpype_java/nbproject/private/ /project/jpype_cpython/nbproject/private/ /project/jpype_cpython/nbproject/Makefile* /project/jpype_java/nbproject/build-impl.xml /project/jpype_java/nbproject/genfiles.properties /project/jpype_java/dist /project/jpype_java/build /project/jpype_cpython/Makefile /project/jpype_cpython/nbproject/Package-Release.bash /project/jpype_python/nbproject/private/ /project/coverage/*.jar *.swp *.stackdump *.zip tmp doc/html native/jars docker/*.tar.gz docker/wheelhouse /.eggs /tmp* a.out .idea/ _build/ jacoco/ wheelhouse/ vc*.pdb *.class jpype-1.5.0/.readthedocs.yml000066400000000000000000000011161453713375400157450ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/conf.py # Optionally build your docs in additional formats such as PDF and ePub # formats: all # Optionally set the version of Python and requirements required to build your docs python: install: - method: pip path: . extra_requirements: - docs build: tools: python: "3.8" os: ubuntu-22.04 apt_packages: - openjdk-8-jdk jpype-1.5.0/AUTHORS.rst000066400000000000000000000005611453713375400145410ustar00rootroot00000000000000Authors ------- The original author: Steve Menard Current Maintainer: Luis Nell Huge thanks to these CONTRIBUTORS: * lazerscience * Koblaid * Michael Willis (michaelwillis) * awesomescot * Joe Quant (joequant) * Mario Rodas * David Moss * Stepan Kolesnik * Philip Smith * Bastian Bowe * Kristi * Martin K. Scherer * Dongwon Shin * rbprogrammer * Karl Einar Nelson jpype-1.5.0/LICENSE000066400000000000000000000227401453713375400136720ustar00rootroot00000000000000============== Apache License ============== :Version: 2.0 :Date: January 2004 :URL: http://www.apache.org/licenses/ ------------------------------------------------------------ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ------------------------------------------------------------ 1. Definitions. --------------- **"License"** shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. **"Licensor"** shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. **"Legal Entity"** shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means *(i)* the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or *(ii)* ownership of fifty percent (50%) or more of the outstanding shares, or *(iii)* beneficial ownership of such entity. **"You"** (or **"Your"**) shall mean an individual or Legal Entity exercising permissions granted by this License. **"Source"** form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. **"Object"** form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. **"Work"** shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). **"Derivative Works"** shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. **"Contribution"** shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." **"Contributor"** shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. ------------------------------ Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. --------------------------- Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. ------------------ You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - You must give any other recipients of the Work or Derivative Works a copy of this License; and - You must cause any modified files to carry prominent notices stating that You changed the files; and - You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - If the Work includes a ``"NOTICE"`` text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such ``NOTICE`` file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a ``NOTICE`` text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the ``NOTICE`` file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the ``NOTICE`` text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. ------------------------------- Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. -------------- This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the ``NOTICE`` file. 7. Disclaimer of Warranty. -------------------------- Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an **"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND**, either express or implied, including, without limitation, any warranties or conditions of **TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE**. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. --------------------------- In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. ---------------------------------------------- While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. **END OF TERMS AND CONDITIONS** jpype-1.5.0/MANIFEST.in000066400000000000000000000003051453713375400144140ustar00rootroot00000000000000recursive-include native *.h *.xml *.java *.jar *.class recursive-include examples * include *.rst doc/* LICENSE # include test/ graft test prune test/classes/* graft setupext global-exclude *.pyc jpype-1.5.0/NOTICE000066400000000000000000000036351453713375400135730ustar00rootroot00000000000000COPYRIGHT ========= Copyright (c) 2004-2008 Steve Menard Copyright (c) 2020 Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory LLNL-CODE- 812311 All rights reserved. LICENSE ======= Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. AUSPICES ======== This work was produced under the auspices of the U.S. Department of Energy by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. This work was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor Lawrence Livermore National Security, LLC, nor any of their employees makes any warranty, expressed or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or Lawrence Livermore National Security, LLC. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or Lawrence Livermore National Security, LLC, and shall not be used for advertising or product endorsement purposes. jpype-1.5.0/README.rst000066400000000000000000000070731453713375400143560ustar00rootroot00000000000000.. image:: doc/logo.png :scale: 50 % :alt: JPype logo :align: center JPype ===== |implementation| |pyversions| |javaversions| |jvm| |platform| |license| JPype is a Python module to provide full access to Java from within Python. It allows Python to make use of Java only libraries, exploring and visualization of Java structures, development and testing of Java libraries, scientific computing, and much more. By gaining the best of both worlds using Python for rapid prototyping and Java for strong typed production code, JPype provides a powerful environment for engineering and code development. This is achieved not through re-implementing Python, as Jython has done, but rather through interfacing at the native level in both virtual machines. This shared memory based approach achieves decent computing performance, while providing the access to the entirety of CPython and Java libraries. :Code: `GitHub `_ :Issue tracker: `GitHub Issues `_ :Discussions: `GitHub Discussions `_ :Documentation: `Python Docs`_ :License: `Apache 2 License`_ :Build status: |TestsCI|_ |Docs|_ :Quality status: |Codecov|_ |lgtm_python|_ |lgtm_java|_ |lgtm_cpp|_ :Version: |PypiVersion|_ |Conda|_ The work on this project began on `Sourceforge `__. LLNL-CODE- 812311 .. |alerts| image:: https://img.shields.io/lgtm/alerts/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18 .. _alerts: https://lgtm.com/projects/g/jpype-project/jpype/alerts/ .. |lgtm_python| image:: https://img.shields.io/lgtm/grade/python/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18&label=python .. _lgtm_python: https://lgtm.com/projects/g/jpype-project/jpype/context:python .. |lgtm_java| image:: https://img.shields.io/lgtm/grade/java/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18&label=java .. _lgtm_java: https://lgtm.com/projects/g/jpype-project/jpype/context:java .. |lgtm_cpp| image:: https://img.shields.io/lgtm/grade/cpp/g/jpype-project/jpype.svg?logo=lgtm&logoWidth=18&label=C++ .. _lgtm_cpp: https://lgtm.com/projects/g/jpype-project/jpype/context:cpp .. |PypiVersion| image:: https://img.shields.io/pypi/v/Jpype1.svg .. _PypiVersion: https://badge.fury.io/py/JPype1 .. |Conda| image:: https://img.shields.io/conda/v/conda-forge/jpype1.svg .. _Conda: https://anaconda.org/conda-forge/jpype1 .. |TestsCI| image:: https://dev.azure.com/jpype-project/jpype/_apis/build/status/jpype-project.jpype?branchName=master .. _TestsCI: https://dev.azure.com/jpype-project/jpype/_build/latest?definitionId=1&branchName=master .. |Docs| image:: https://img.shields.io/readthedocs/jpype.svg .. _Docs: http://jpype.readthedocs.org/en/latest/ .. |Codecov| image:: https://codecov.io/gh/jpype-project/jpype/branch/master/graph/badge.svg .. _Codecov: https://codecov.io/gh/jpype-project/jpype .. |implementation| image:: https://img.shields.io/pypi/implementation/jpype1.svg .. |pyversions| image:: https://img.shields.io/pypi/pyversions/jpype1.svg .. |javaversions| image:: https://img.shields.io/badge/java-8%20%7C%209%20%7C%2011-purple.svg .. |jvm| image:: https://img.shields.io/badge/jvm-Open%20%7C%20Oracle%20%7C%20Corretto-purple.svg .. |platform| image:: https://img.shields.io/conda/pn/conda-forge/jpype1.svg .. |license| image:: https://img.shields.io/github/license/jpype-project/jpype.svg .. _Apache 2 License: https://github.com/jpype-project/jpype/blob/master/LICENSE .. _Python Docs: http://jpype.readthedocs.org/en/latest/ SPDX-License-Identifier: Apache-2.0 jpype-1.5.0/codecov.yml000066400000000000000000000011601453713375400150230ustar00rootroot00000000000000codecov: require_ci_to_pass: yes coverage: precision: 2 round: down range: "70...100" status: project: default: false python: target: 85% threshold: 1% paths: "jpype/" cpp: target: 80% threshold: 1% paths: - "native/common/" - "native/python/" java: target: 75% threshold: 2% paths: "native/java/" parsers: gcov: branch_detection: conditional: yes loop: yes method: no macro: no comment: layout: "reach,diff,flags,tree" behavior: default require_changes: no jpype-1.5.0/coverage.sh000066400000000000000000000013301453713375400150040ustar00rootroot00000000000000#!/bin/sh # Script for testing coverage locally PYTHON=python PIP="python -m pip" ./resolve.sh $PYTHON setup.py test_java $PIP install gcovr pytest-cov jedi $PYTHON setup.py --enable-coverage --enable-build-jar build_ext --inplace $PYTHON -m pytest -rsx -v test/jpypetest \ --cov-report=xml:coverage_py.xml \ --cov-report=html:build/coverage/python \ --cov=jpype \ --classpath="build/classes" --jacoco --checkjni java -jar lib/org.jacoco.cli-0.8.5-nodeps.jar report build/coverage/jacoco.exec \ --classfiles build/classes/ \ --html build/coverage/java \ --sourcefiles native/java mkdir build/coverage/cpp gcovr -r . --html-details -o build/coverage/cpp/jpype.html --exclude-unreachable-branches --exclude-throw-branches jpype-1.5.0/dev-requirements.txt000066400000000000000000000000521453713375400167150ustar00rootroot00000000000000typing-extensions ; python_version< "3.8" jpype-1.5.0/doc/000077500000000000000000000000001453713375400134255ustar00rootroot00000000000000jpype-1.5.0/doc/CHANGELOG.rst000066400000000000000000000753431453713375400154620ustar00rootroot00000000000000Changelog ========= This changelog *only* contains changes from the *first* pypi release (0.5.4.3) onwards. Latest Changes: - **1.5.0 - 2023-04-03** - Support for Python 3.12 - Switched ``__eq__`` and ``__ne__`` operator to use ``equals`` rather than ``compareTo`` for comparable objects to avoid exception when comparing object of different types. - Fixed segmentation fault when comparing Java Comparable to primitives. - Java exceptions that occur in inequality comparisons now map to Python TypeError. - Fixed crash when calling subscript on JArray. - Fixed direct byte buffers not reporting nbytes correctly when cast to memoryview. - Expand the defintion for Functional interface to include classes without FunctionInterface annotation. - Add additional matching level for derived types to resolve ambiguities when a derived type is used in place of base class when determining the method overload. This will resolve some previous ambiguities between methods. - **1.4.1 - 2022-10-26** - Fixed issue with startJVM changing locale settings. - Changes to support Python 3.11 - Fix truncation of strings on null when using convert strings. - Replaced distutil with packaging - **1.4.0 - 2022-05-14** - Support for all different buffer type conversions. - Improved buffer transfers to numpy as guaranteed to match Java types. However, exact dtype for conversions is os/numpy version dependent. - Support for byte order channels on buffer transfers. - Byte size for buffers now fixed to Java definitions. - When directly accessing Java arrays using memory view, Python requires a cast from buffers. Required because Python does not support memory view alterations on non-native sizes. - Fix crash when comparing JChar. - Order handling for numerical operations with JChar fixed. - Improved matching for Java functors based on parameter count. - Dropped support for Python 3.5 and 3.6 - dbapi2 handles drivers that don't support autocommit. - Fixed issue when Java classes with dunder methods such as ``__del__`` caused conflicts in Python type system. Java method which match dunder patterns are longer translated to Python. - Fix issue with numpy arrays with no dimensions resulting in crash. - Support for user defined conversions for java.lang.Class and array types. - Fixed issue with ssize_t on Windows for Python 3.10. - **1.3.0 - 2021-05-19** - Fixes for memory issues found when upgrading to Python 3.10 beta. - Add additional diagnositics for importing of non-public class. - Fixed issue with classes with unsatified dependencies leading to a crash on windows. - Fixed a bug with arrays created using the short cut. The wrong type was being returned. - **1.2.1 - 2021-01-02** - Missing stub files added. - Python 3.9 issues are resolved on Windows. - JPype scans jar files and rebuilding missing directories to allow imports from stripped and obfuscated jar files. - **1.2.0 - 2020-11-29** - Added builds for Python 3.9. Python 3.9 on Windows is currently failing due to issue in Python. - Fixed bug when importing from multi-release jars. The directory was being truncated to only those classes in the overlay. - addClassPath can add jar files after the JVM is started. The default loader for JPype class is ``org.jpype.classloader.DynamicClassLoader``. - Build support of z/OS added. - Bug causing ambiguity between primitives and variadic arguments in method resolution was corrected. - Boolean was inadvertently left out of method resolution. ``boolean`` now properly matched with boxed types. - Support for PyInstaller was added. - **1.1.2 - 2020-10-23** - Linux binaries are now stripped for size. - Add importlib.util to address instability in Python importlib boot process. Certain versions of Python such as 3.9 appear to not properly load this module resulting in unexpected errors during startJVM. - **1.1.1 - 2020-10-21** - Fixed packaging problem on linux. - **1.1.0 - 2020-10-13** - Correct bug resulting in reporting ambiguous overloads when resolving methods with variadic arguments. - Ctrl+C behavior is switchable with interrupt flag to startJVM. If True, process will halt on Ctrl-C. If False, the process will transfer control to Python rather than halting. If not specified JPype will assume false if Python is started as an interactive shell. - Fixed crash with Ctrl+C when multiple exceptions were generated. - Removed extraneous exception when calling Ctrl+C before Java code is executed for methods and fields. - Fixed memory leak with string cache. - Fixed crash when manually creating wrappers for anonymous classes. - Fixed reference count problem in stackframes used for exceptions. - Errors report `*static*` when the matching with a static method so that it is clear when a member method was called statically. - java.lang.String slices function like Python string slice. - Java packages now operate as normal Python modules. Removed restrictions regarding setattr. All package instances for the same package name are shared so that functionality added to one instance is shared wiht all instances. - **1.0.2 - 2020-07-27** - The wrapper for Throwable was getting the wrapper for Object rather than the expected wrapper resulting in odd conversions from Python classes. - Typos within the import system resulting in "jname" not found corrected. - ^C propogates to a KeyboardInterrupt properly. - Added cache to the method dispatch to bypass resolution of overloads. This reduces the cost of method resolution significantly especially if the same overload is hit repeatedly such as during loop operations. - Improved speed on transfer of lists, tuples, buffers to arrays of Java primitives by a factor of 4 to 100 depending on the data type. The conversion uses optimized path for memory buffers, rather than the Sequence API. When a Python buffer is encountered only the first element is checked for conversion as Python buffers are homogeneous. - Corrected symbol problem with Python 3.5.3. PySlice_Unpack was introduced in a later patch release and should not have been used. - **shutdown** The behavior log entry for changes on shutdown were lost in the 1.0 release. JPype now calls the JVM shutdown routine which tries to gracefully exit when shutdown is called. This results in several changes in behavior. Non daemon threads can now hold open the JVM until they have completed. Proxy calls will hold the shutdown until the call is completed but will receive an interrupt message. Files now close properly and will flush if the threads properly handle the exception. Resource clean up hooks and finalizers are executed. AtExit hooks in the JVM are called as spawned threads. Automatic attachment of threads by use of the JVM from Python are done as daemon but can be reattached as user threads on demand. Buggy code that fails to properly handle thread clean up will likely hang on shutdown. Additional documentation is located in the user guide. - A bug was reported with numpy.linalg.inv resulting in crashes. This was traced to an interaction with threading between the JVM and some compilations of numpy. The workaround appears to be calling numpy.linalg.inv prior to starting the JVM. - **1.0.1 - 2020-07-16** - Workarounds for Python 3.8.4 release. Python altered logic regarding the use of ``__setattr__`` for object and type, preventing it from being used to alter derived classes. Also the checking for errors was delegated from the ``__setattr__`` method so exception types on some sanity checks needed to be updated accordingly. - **1.0.0 - 2020-07-12** - ``JChar`` is supported as a return type, thus rather than returning a string where a ``JChar`` is expected. For compatiblity ``JChar`` is derived from ``str`` and implements implicit conversion to an ``int`` when used in numeric operations. Therefore, it passes the return, argument, and field contracts. But that means it is no longer considered a numerical type to Python and thus ``isinstance(c, int)`` is False. This is consistent with the Java type conversion rules. - Introduced Python operator for Java casting. In Java to cast to a type you would use ``(Type) obj``, but Python does not support anything similar. Therefore, we are enlisting the rarely used ``matmul`` operator as to allow an easy way to cast an object to a Java type. When a cast to a Java type is required, use ``Type@obj`` or ``(Type)@obj``. - Introduced array notation to create Java arrays. In earlier versions, JArray factory was required to make a new array type. But this is tedious to read. In Java the notation would be ``Type[]`` to declare a type or ``new Type[sz]`` to make a new array. Python does not directly support this notation, but it does allow for unspecifed array sizes using a slice. All Java class types support ``Type[sz]`` to create an array of a fixed size and ``Type[:]`` to create an array type which can be intiated later. This call be applied to multiple dimensions to create fixed sized arrays ``Type[s1][s2][s3]`` to declare multidimension array types ``Type[:][:][:]`` or to create a new multi dimensional array with unspecified dimensions ``Type[sz][:][:]``. Applying a slice with limits to a class is unsupported. - Java classes annotated with ``@FunctionalInterface`` can be converted from any Python object that implements ``__call__``. This allows functions, lambdas, and class constructors to be used whereever Java accepts a lambda. - Support for Protocol on type conversions. Attribute based conversions deprecated in favor of Protocol. Internal API for stubbing. - Deprecated class and functions were removed. ``JIterator``, use of ``JException`` as a factory, ``get_default_jvm_path``, ``jpype.reflect`` module. - Default for starting JVM is now to return Java strings rather than convert. - Python deprecated ``__int__`` so implicit conversions between float and integer types will produce a ``TypeError``. - Use of ``JException`` is discouraged. To catch all exceptions or test if an object is a Java exception type, use ``java.lang.Throwable``. - Chained Java exception causes are now reflected in the Python stackframes. - Use of ``JString`` is discouraged. To create a Java string or test if an object is a Java string type, use ``java.lang.String``. - Updated the repr methods on Java classes. - ``java.util.List`` completes the contract for ``collections.abc.Sequence`` and ``collections.abc.MutableSequence``. - ``java.util.Collection`` completes the contract for ``collections.abc.Collection``. - Java classes are closed and will raise ``TypeError`` if extended in Python. - Handles Control-C gracefully. Previous versions crash whenever Java handles the Control-C signal as they would shutdown Java during a call. Now JPype will produce a ``InterruptedException`` when returning from Java. Control-C will not break out of large Java procedures as currently implemented as Java does not have a specific provision for this. - **0.7.5 - 2020-05-10** - Updated docs. - Fix corrupt conda release. - **0.7.4 - 4-28-2020** - Corrected a resource leak in arrays that affects array initialization, and variable argument methods. - Upgraded diagnostic tracing and JNI checks to prevent future resource leaks. - **0.7.3 - 4-17-2020** - **Replaced type management system**, memory management for internal classes is now completely in Java to allow enhancements for buffer support and revised type conversion system. - Python module ``jpype.reflect`` will be removed in the next release. - ``jpype.startJVM`` option ``convertStrings`` default will become False in the next release. - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` is deprecated to support casting to Python wrapper types in Java in a future release. - Dropped support for Cygwin platform. - ``JFloat`` properly follows Java rules for conversion from ``JDouble``. Floats outside of range map to inf and -inf. - ``java.lang.Number`` converts automatically from Python and Java numbers. Java primitive types will cast to their proper box type when passed to methods and fields taking Number. - ``java.lang.Object`` and ``java.lang.Number`` box signed, sized numpy types (int8, int16, int32, int64, float32, float64) to the Java boxed type with the same size automatically. Architecture dependent numpy types map to Long or Double like other Python types. - Explicit casting using primitives such as JInt will not produce an ``OverflowError``. Implicit casting from Python types such as int or float will. - Returns for number type primitives will retain their return type information. These are derived from Python ``int`` and ``float`` types thus no change in behavior unless chaining from a Java methods which is not allowed in Java without a cast. ``JBoolean`` and ``JChar`` still produce Python types only. - Add support for direct conversion of multi-dimensional primitive arrays with ``JArray.of(array, [dtype=type])`` - ``java.nio.Buffer`` derived objects can convert to memoryview if they are direct. They can be converted to NumPy arrays with ``numpy.asarray(memoryview(obj))``. - Proxies created with ``@JImplements`` properly implement ``toString``, ``hashCode``, and ``equals``. - Proxies pass Python exceptions properly rather converting to ``java.lang.RuntimeException`` - ``JProxy.unwrap()`` will return the original instance object for proxies created with JProxy. Otherwise will return the proxy. - JProxy instances created with the ``convert=True`` argument will automatic unwrap when passed from Java to Python. - JProxy only creates one copy of the invocation handler per garbage collection rather than once per use. Thus proxy objects placed in memory containers will have the same object id so long as Java holds on to it. - jpype.imports and JPackage verify existance of packages and classes. Imports from Java packages support wildcards. - Bug with JPackage that imported private and protected classes inappropriately has been corrected. Protected classes can still be imported using JClass. - Undocumented feature of using a Python type in ``JObject(obj, type=tp)`` is deprecated to support casting to Python wrapper types in Java in a - ``@JImplements`` with keyword argument ``deferred`` can be started prior to starting the JVM. Methods are checked at first object creation. - Fix bug that was causing ``java.lang.Comparable``, ``byte[]``, and ``char[]`` to be unhashable. - Fix bug causing segfault when throwing Exceptions which lack a default constructor. - Fixed segfault when methods called by proxy have incorrect number of arguments. - Fixed stack overflow crash on iterating ImmutableList - ``java.util.Map`` conforms to Python ``collections.abc.Mapping`` API. - ``java.lang.ArrayIndexOutOfBoundsException`` can be caught with ``IndexError`` for consistency with Python exception usage. - ``java.lang.NullPointerException`` can be caught with ``ValueError`` for consistency with Python exception usage. - **Replaced type conversion system**, type conversions test conversion once per type improving speed and increasing flexiblity. - User defined implicit conversions can be created with ``@JConversion`` decorator on Python function taking Java class and Python object. Converter function must produce a Java class instance. - ``pathlib.Path`` can be implicitly converted into ``java.lang.File`` and ``java.lang.Path``. - ``datetime.datatime`` can implicitly convert to ``java.time.Instant``. - ``dict`` and ``collections.abc.Mapping`` can convert to ``java.util.Map`` if all element are convertable to Java. Otherwise, ``TypeError`` is raised. - ``list`` and ``collections.abc.Sequence`` can convert to ``java.util.Collection`` if all elements are convertable to Java. Otherwise, ``TypeError`` is raised. - **0.7.2 - 2-28-2020** - C++ and Java exceptions hold the traceback as a Python exception cause. It is no longer necessary to call stacktrace() to retrieve the traceback information. - Speed for call return path has been improved by a factor of 3. - Multidimensional array buffer transfers increase speed transfers to numpy substantially (orders of magnitude). Multidimension primitive transfers are read-only copies produced inside the JVM with C contiguous layout. - All exposed internals have been replaced with CPython implementations thus symbols `__javaclass__`, `__javavalue__`, and `__javaproxy__` have been removed. A dedicated Java slot has been added to all CPython types derived from `_jpype` class types. All private tables have been moved to CPython. Java types must derive from the metaclass `JClass` which enforces type slots. Mixins of Python base classes is not permitted. Objects, Proxies, Exceptions, Numbers, and Arrays derive directly from internal CPython implementations. See the :doc:`ChangeLog-0.7.2` for details of all changes. - Internal improvements to tracing and exception handling. - Memory leak in convertToDirectBuffer has been corrected. = Arrays slices are now a view which support writeback to the original like numpy array. Array slices are no longer covariant returns of list or numpy.array depending on the build procedure. - Array slices support steps for both set and get. - Arrays now implement `__reversed__` - Incorrect mapping of floats between 0 and 1 to False in setting Java boolean array members is corrected. - Java arrays now properly assert range checks when setting elements from sequences. - Java arrays support memoryview API and no longer required NumPy to transfer buffer contents. - Numpy is no longer an optional extra. Memory transfer to NumPy is available without compiling for numpy support. - JInterface is now a meta class. Use ``isinstance(cls, JInterface)`` to test for interfaces. - Fixed memory leak in Proxy invocation - Fixed bug with Proxy not converting when passed as an argument to Python functions during execution of proxies - Missing tlds "mil", "net", and "edu" added to default imports. - Enhanced error reporting for UnsupportedClassVersion during startup. - Corrections for collection methods to improve complience with Python containers. - java.util.Map gives KeyError if the item is not found. Values that are ``null`` still return ``None`` as expected. Use ``get()`` if empty keys are to be treated as ``None``. - java.util.Collection ``__delitem__`` was removed as it overloads oddly between ``remove(Object)`` and ``remove(int)`` on Lists. Use Java ``remove()`` method to access the original Java behavior, but a cast is strongly recommended to to handle the overload. - java.lang.IndexOutOfBoundsException can be caught with IndexError for complience when accessing ``java.util.List`` elements. - **0.7.1 - 12-16-2019** - Updated the keyword safe list for Python 3. - Automatic conversion of CharSequence from Python strings. - java.lang.AutoCloseable supports Python "with" statement. - Hash codes for boxed types work properly in Python 3 and can be used as dictionary keys again (same as JPype 0.6). Java arrays have working hash codes, but as they are mutable should not be used as dictionary keys. java.lang.Character, java.lang.Float, and java.lang.Double all work as dictionary keys, but due to differences in the hashing algorithm do not index to the same location as Python native types and thus may cause issues when used as dictionary keys. - Updated getJVMVersion to work with JDK 9+. - Added support for pickling of Java objects using optional module ``jpype.pickle`` - Fixed incorrect string conversion on exceptions. `str()` was incorrectly returning `getMessage` rather than `toString`. - Fixed an issue with JDK 12 regarding calling methods with reflection. - Removed limitations having to do with CallerSensitive methods. Methods affected are listed in :doc:`caller_sensitive`. Caller sensitive methods now receive an internal JPype class as the caller - Fixed segfault when converting null elements while accessing a slice from a Java object array. - PyJPMethod now supports the FunctionType API. - Tab completion with Jedi is supported. Jedi is the engine behind tab completion in many popular editors and shells such as IPython. Jedi version 0.14.1 is required for tab completion as earlier versions did not support annotations on compiled classes. Tab completion with older versions requires use of the IPython greedy method. - JProxy objects now are returned from Java as the Python objects that originate from. Older style proxy classes return the inst or dict. New style return the proxy class instance. Thus proxy classes can be stored on generic Java containers and retrieved as Python objects. - **0.7.0 - 2019** - Doc strings are generated for classes and methods. - Complete rewrite of the core module code to deal unattached threads, improved hardening, and member management. Massive number of internal bugs were identified during the rewrite and corrected. See the :doc:`ChangeLog-0.7` for details of all changes. - API breakage: - Java strings conversion behavior has changed. The previous behavior was switchable, but only the default convert to Python was working. Converting to automatically lead to problems in which is was impossible to work with classes like StringBuilder in Java. To convert a Java string use ``str()``. Therefore, string conversion is currently selected by a switch at the start of the JVM. The default shall be False starting in JPype 0.8. New code is encouraged to use the future default of False. For the transition period the default will be True with a warning if not policy was selected to encourage developers to pick the string conversion policy that best applies to their application. - Java exceptions are now derived from Python exception. The old wrapper types have been removed. Catch the exception with the actual Java exception type rather than ``JException``. - Undocumented exceptions issued from within JPype have been mapped to the corresponding Python exception types such as ``TypeError`` and ``ValueError`` appropriately. Code catching exceptions from previous versions should be checked to make sure all exception paths are being handled. - Undocumented property import of Java bean pattern get/set accessors was removed as the default. It is available with ``import jpype.beans``, but its use is discouraged. - API rework: - JPype factory methods now act as base classes for dynamic class trees. - Static fields and methods are now available in object instances. - Inner classes are now imported with the parent class. - ``jpype.imports`` works with Python 2.7. - Proxies and customizers now use decorators rather than exposing internal classes. Existing ``JProxy`` code still works. - Decorator style proxies use ``@JImplements`` and ``@JOverload`` to create proxies from regular classes. - Decorator style customizers use ``@JImplementionFor`` - Module ``jpype.types`` was introduced containing only the Java type wrappers. Use ``from jpype.types import *`` to pull in this subset of JPype. - ``synchronized`` using the Python ``with`` statement now works for locking of Java objects. - Previous bug in initialization of arrays from list has been corrected. - Added extra verbiage to the to the raised exception when an overloaded method could not be matched. It now prints a list of all possible method signatures. - The following is now DEPRECATED - ``jpype.reflect.*`` - All class information is available with ``.class_`` - Unncessary ``JException`` from string now issues a warning. - The followind is now REMOVED - Python thread option for ``JPypeReferenceQueue``. References are always handled with with the Java cleanup routine. The undocumented ``setUsePythonThreadForDaemon()`` has been removed. - Undocumented switch to change strings from automatic to manual conversion has been removed. - Artifical base classes ``JavaClass`` and ``JavaObject`` have been removed. - Undocumented old style customizers have been removed. - Many internal jpype symbols have been removed from the namespace to prevent leakage of symbols on imports. - promoted *`--install-option`* to a *`--global-option`* as it applies to the build as well as install. - Added *`--enable-tracing`* to setup.py to allow for compiling with tracing for debugging. - Ant is required to build jpype from source, use ``--ant=`` with setup.py to direct to a specific ant. - **0.6.3 - 2018-04-03** - Java reference counting has been converted to use JNI PushLocalFrame/PopLocalFrame. Several resource leaks were removed. - ``java.lang.Class<>.forName()`` will now return the java.lang.Class. Work arounds for requiring the class loader are no longer needed. Customizers now support customization of static members. - Support of ``java.lang.Class<>`` - ``java.lang.Object().getClass()`` on Java objects returns a java.lang.Class rather than the Python class - ``java.lang.Object().__class__`` on Java objects returns the python class as do all python objects - ``java.lang.Object.class_`` maps to the java statement ``java.lang.Object.class`` and returns the ``java.lang.Class`` - java.lang.Class supports reflection methods - private fields and methods can be accessed via reflection - annotations are avaiable via reflection - Java objects and arrays will not accept setattr unless the attribute corresponds to a java method or field whith the exception of private attributes that begin with underscore. - Added support for automatic conversion of boxed types. - Boxed types automatically convert to python primitives. - Boxed types automatically convert to java primitives when resolving functions. - Functions taking boxed or primitives still resolve based on closest match. - Python integer primitives will implicitly match java float and double as per Java specification. - Added support for try with resources for ``java.lang.Closeable``. Use python "with MyJavaResource() as resource:" statement to automatically close a resource at the end of a block. - **0.6.2 - 2017-01-13** - Fix JVM location for OSX. - Fix a method overload bug. - Add support for synthetic methods - **0.6.1 - 2015-08-05** - Fix proxy with arguments issue. - Fix Python 3 support for Windows failing to import winreg. - Fix non matching overloads on iterating java collections. - **0.6.0 - 2015-04-13** - Python3 support. - Fix OutOfMemoryError. - **0.5.7 - 2014-10-29** - No JDK/JRE is required to build anymore due to provided jni.h. To override this, one needs to set a JAVA_HOME pointing to a JDK during setup. - Better support for various platforms and compilers (MinGW, Cygwin, Windows) - **0.5.6 - 2014-09-27** - *Note*: In this release we returned to the three point number versioning scheme. - Fix #63: 'property' object has no attribute 'isBeanMutator' - Fix #70: python setup.py develop does now work as expected - Fix #79, Fix #85: missing declaration of 'uint' - Fix #80: opt out NumPy code dependency by '--disable-numpy' parameter to setup. To opt out with pip append --install-option="--disable-numpy". - Use JVMFinder method of @tcalmant to locate a Java runtime - **0.5.5.4 - 2014-08-12** - Fix: compile issue, if numpy is not available (NPY_BOOL n/a). Closes #77 - **0.5.5.3 - 2014-08-11** - Optional support for NumPy arrays in handling of Java arrays. Both set and get slice operators are supported. Speed improvement of factor 10 for setting and factor 6 for getting. The returned arrays are typed with the matching NumPy type. - Fix: add missing wrapper type 'JShort' - Fix: Conversion check for unsigned types did not work in array setters (tautological compare) - **0.5.5.2 - 2014-04-29** - Fix: array setter memory leak (ISSUE: #64) - **0.5.5.1 - 2014-04-11** - Fix: setup.py now runs under MacOSX with Python 2.6 (referred to missing subprocess function) - **0.5.5 - 2014-04-11** - *Note* that this release is *not* compatible with Python 2.5 anymore! - Added AHL changes * replaced Python set type usage with new 2.6.x and higher * fixed broken Python slicing semantics on JArray objects * fixed a memory leak in the JVM when passing Python lists to JArray constructors * prevent ctrl+c seg faulting * corrected new[]/delete pairs to stop valgrind complaining * ship basic PyMemoryView implementation (based on numpy's) for Python 2.6 compatibility - Fast sliced access for primitive datatype arrays (factor of 10) - Use setter for Java bean property assignment even if not having a getter by @baztian - Fix public methods not being accessible if a Java bean property with the same name exists by @baztian (*Warning*: In rare cases this change is incompatibile to previous releases. If you are accessing a bean property without using the get/set method and the bean has a public method with the property's name you have to change the code to use the get/set methods.) - Make jpype.JException catch exceptions from subclasses by @baztian - Make more complex overloaded Java methods accessible (fixes https://sourceforge.net/p/jpype/bugs/69/) by @baztian and anonymous - Some minor improvements inferring unnecessary copies in extension code - Some JNI cleanups related to memory - Fix memory leak in array setters - Fix memory leak in typemanager - Add userguide from sourceforge project by @baztian - **0.5.4.5 - 2013-08-25** - Added support for OSX 10.9 Mavericks by @rmangino (#16) - **0.5.4.4 - 2013-08-10** - Rewritten Java Home directory Search by @marsam (#13, #12 and #7) - Stylistic cleanups of setup.py - **0.5.4.3 - 2013-07-27** - Initial pypi release with most fixes for easier installation jpype-1.5.0/doc/ChangeLog-0.7.2.rst000066400000000000000000000571451453713375400164640ustar00rootroot00000000000000:orphan: Buffers and NumPy removal ========================= NumPy was used primarily for supplying Python buffers on slicing. Numpy has always been problematic for JPype. As an optional extra is may or may not be built into the distribution, but if it is compiled in it is required. Therefore, is itn't really on "extra". Therefore, removing it would make distribution for binary versions of JPype much easier. NumPy returns on slicing is but one part of the three uses of Python buffers in JPype. Thus to properly remove it we need to rework to remove it we need to review all three of the paths. These paths are - Conversion of Python buffers to Java arrays on setArrayRange. - Conversion of Java arrays to slices (and then from slices to buffers so that they can be transferred to NumPy.) - Connecting bytearray to Java direct byte buffers. We reviewed and revised each of the paths accordingly. On conversion of Python buffers, the implementation was dated from Python 2.6 era where there was no formal support for buffers. Thus the buffer implementation never consulted the buffer type to see what type of object was being transferred nor how the memory was oriented. This entire section had to be replaced and was given the same conversion rules as NumPy. Any conversion is possible (including lossy ones like float to bool). The rules to trigger the conversion is by slicing just as with NumPy. By replicating the rules of NumPy we hide the fact that NumPy is no longer used and increase typesafety. There was a number of cases where in the past it would reinterpret cast the memory that will now function properly. The old behavior was a useless side effect of the implementation and was unstable with machine architecture so not likely used be a user. The getArrayRange portion has to be split into two pieces. Under the previous implementation the type of the return changes from Python list to NumPy array depending on the compile option. Thus the test suite tested different behaviors for each. In removing NumPy we replace the Java array slice return with a Java array. Thus the type is always consistent. The Java array that is returned is still backed by the same array as before and has the start, end, and step set appropriately. This does create one change in behavior as the slice now has left assignment (just like NumPy) and before it was a copy. It is difficult exercise this but to do so we have to copy a slice to a new variable then use a second array dereference to assign an element. Because we converted to either list or NumPy, the second dereference could not affect the original. There is no way to avoid this behavior change without adding a large transaction cost. Which is why NumPy has the exact same behavior as our replacement implementation. We could in principle make the slice read only rather than allowing for double slicing, but that would also be an API change. There is one other consequence of producing a view of the original having to do with passing back to Java. As Java does not recognize the concept of an array view we must force it back to a Java type at the JNI level. We will force copy the array slice into a Java array at that time. Thus replicates the same functionality. This induces one special edge case as `java.lang.Object` and `java.lang.Serializable` which are both implemented by arrays must be aware of the slice copy. Before we trigger the conversion to NumPy or list by calling the slice without limits operator `[:]` on the array. Under the new implementation this is effectively a no-op. Thus we haven't broken or forced any changes in the API. The third case of direct byte buffers also revealed problems. Again the type of the buffer was not being check resulting in weird reinterpret cast like behaviors. Additionally, the memory buffer interface was making a very bad assumption about the referencing of the buffer by assuming that referencing the object that holds the buffer is the same as referencing the buffer itself. It was working only because the buffer was being leaked entirely and was likely possible to break under situations as the leaked buffer essentially locked the object forever. To implement all of this properly unfortunately requires making the Python wrapper of Java arrays a direct type. This is possible in JPype 0.8 series where we converted all classes to CPython roots, thus our only choice is to backport the JPype speed patch into JPype 0.7. API change summary ------------------ - The type of a slice is no longer polymorphic but is always a Java array now. - The unbounded slice is now a no op. - Buffer types now trigger conversion routines rather than reinterpret casting the memory. - Direct buffers now guard the memory layout such that they work only with mutable bytearray like types. - Assignment of elements though double slicing of an array now affects the original rather than just doing nothing effective like before. JPype Speed Patch ================= Speed has always been an issue for JPype. While the interface of JPype is great the underlying implementation is wanting. Part of this was choices made early in the development that favors ease of implementation over speed. This forced a very thin interface using Python capsules and then the majority of the code in a pure Python module. The speed issue stems from two main paths. The first being the method resolution phase in which we need to consider each method overload argument by argument which means many queries applied to each object. The second is the object construction penality when we return a Java object back to Python. The object bottleneck is both on the cost of the wrappers we produce, but additionaly all of the objects that are constructed to communicate with a Python function. Every int, list, string and tuple we use in the process is another object to construct. Thus returning one object triggers dozens of object to be constructed. We have addressed these problems in five ways - Improve resolution of methods by removing the two phase approach to resolving a type match cutting the resolution time in half. - Caching the types in C so that they don't have to go back to Python to execute a method to check the Python cache and construct if necessary. - Converting all of the base types to CPython so that they can directly access the C++ guts without going back through entry points. - Remove all Python new and init method from the Java class tree so that we don't leave C during the construction phase. Thus avoiding having to construct Python objects for each argument used during object construction. - Adding a Java slot to so that we can directly access Java resources both increasing the speed of the queries and saving us an additional object during construction. All of these are being implemented for JPype 0.8 series. For now we are backporting the last four to the JPype 0.7 series. The first is not possible to backport as it requires larger structural changes. Lets briefly discuss each of the items Method resolution ----------------- During method resolution the previous implementation had a two phase approach. First is tried to match the arguments to the Java types in canConvertToJava. In this each Java argument had to walk through each possible type conversion and decide if the conversion is possible. Once the we have the method overload resovled we then perform the conversion by calling convertToJava. That would then walk through each possible type conversion to find the right one and apply it. Thus we did tha work twice. To prevent this from happening we need to reserve memory for each argument we are trying to convert. When we walk through the list and find a conversion rather than just returning true, we place a pointer to the conversion routine. That way when we call convertToJava we don't have to walk the list a second time but instead go straight to the pointer to get the routine to execute. This change has two additional consequences. First the primary source of bugs in the type conversion was a mismatch between canConvertToJava and convertToJava thus we are removing that problem entirely. The second and more important to the user is that the type system is now open. By installing a routine we can now add a user rule. Therefore if we need `java.sql.TimeStamp` to accept a Python time object we just need to add this to the type conversion table at the Python level. This is implemented in the ClassHints patch. About half of our customizer code was to try to achieve this on a per method level. Thus this elimiates a lot of our current Python customizer code. The remaining customizer code is to rename Java methods to Python methods and that will remain. Caching of Python wrappers -------------------------- In the previous implementation there was a text keyed dictionary that was consulted to get type wrappers. To access it C++ called to a Python function that decided when to return a cached type and when to create a new one. This meant dozens of object constructed just to find the wrapper. To solve this we simply move the cache and add it to the JClass directly. We have to back reference the Python class so it can't go away while the JVM is running. There is one section of code that also uses the wrapper dict in the customizers which needs to decided does a wrapper already exist for the customizer. We have replaced these calls with methods on the module. Conversion of the Base classes ------------------------------ JPype has a number of base classes (object, primitive, exception, string, array) which hold the methods for the class. If they are implemented as pure Python than every access from C++ to these elements needs to create objects accordingly when then are passed back through the module entry points to get back to C++. We can avoid this by implementing each of these in CPython first at the module layer and then extending them in the exposed module so that they have the same outward appearance as before. We made one refinement during the conversion by implementing all of the CPython classes using the Heep type API which has the distinct advantage that unlike static types, it can be changed at runtime. Thus from Python we can add behavior to the heap types simply with by using `type.__setattr_`. This was a bit of a challenge as the documentation on heap types is much more sparse than for static types. However, after going through the process I would recommend that all new CPython modules should use heap types rather than static as API is much better and the result much more flexable and stable. The only downside being the memory footprint increases from 400 bytes to 900 bytes. There are a few rough spots in the heap type API in that certain actions like setting the Buffer have to be added to the type after creation, but otherwise it is a big improvement. Now if all of the documentation would just drop the old static API in favor of heap types it would be great. Constructor simplifications --------------------------- In order to benefit from moving all of the base classes to C, we have to make sure that derived classes do not transfer control back to Python. Currently this happens due to the factory nature of our classes. The entry point for JObject is shared between the construction of objects from Python and a return from Java. Thus we have to either separate the factory behavior by pushing those types out of the type tree or pushing the factory behavior into the C layer. We have chosen to split the factories and use overrides of the type system in the meta class to apply `isinstance` and `issubtype` behavior. We can further restrict the type system if we need to by adding verifications that the `__new__` and `__init__` methods must point the original base class implementations if need. Howver, we have not taken this step as of yet. The split approach effectively removes these heavy elements from type creation. The concequence of this is that means all of the rest of logic needs to be in CPython implementation. These can be rather cumbersome at times. It is always a slippery slope when pushing code from Python back to CPython. Some thing are needed as they are on the critical path while others are called only occasionally and thus represent no cost to leave in Python. On the other hand some things are easy to implement in CPython because the have direct access rather than having to go through a module entry point. We have gone with the approach that all critical path and all code the eliminates the need for an entry point should be pushed back to C. Java Slots ------------- In order to get any reasonable speed with Python, the majority of the code needs to be in C. But additionally there needs to be the use of slots which are hard coded locations to search for a particular piece of information. This presents a challenge when wrapping Java as we need a slot for the Java value structure which must appear on Python object, long, float, exception, and type. These different types each have their own memory layout and thus we can't just add the slot at the base as once something is added to the base object it can no longer be used in multiple inheritance. Thus we require a different approach. Looking through the CPython source, we find they have the same quandary with respect to `__dict__` and `__weakref__` slots. Those slots do not appear one the base object but get added along the way. The method they use is to add those slots to the back of the object be increasing the basesize of the object and then referencing them with two offset slots in the type object. If the type is variable length the slot offset are negative thus referencing from the end of the object, or positive if the object is a fixed layout. Thus we tried a few formulations to see what would work best. Broken tree approach ~~~~~~~~~~~~~~~~~~~~ The problem with just directly adding the slots in the tree is that the Java inheritance tree forces the order of the Python tree we have to apply. If we add a slot to `java.lang.Object` we have to keep the slot on `java.lang.Throwable` but that is not possible because Throwable requires it to be derived from Python `Exception`. Thus if we are going to add a slot to the base we would have to break the tree into pieces on the Python side. This is possible due to Python inheritance hacking with some effort. But this approach had significant down sides. When we go to access the slot we have to first figure out if the slot is present and if not then fall back to looking in the dictionary. But one of the most common cases is one in which the item has no slot at all. Thus if we have to both look for the slot and then hit the dictionary, this is worse than just going to the dictionary in the first place. Thus this defeats the point of a slot in many cases. Python dict approach ~~~~~~~~~~~~~~~~~~~~~~~~~ We attempted the same trick by increasing the basesize to account for our extra slot. This leaves to difficulties. First, the slot has no offset so we need to find it each time by implying its location. Second, the extra objects have to be "invisible" during the type construction phase, or Python will conclude the memory layout of the object is in conflict. We can fool the type system by subtracting the extra space from the type during the declaration phase and then adding it back after the base types are created. This approach failed because the "invisible" part is checked each and every time a new type is added to the system. Thus every dynamic type we add checks the base types for consistency and at some point the type system will find the inconsistency and cause a failure. Therefore, this system can never be robust. Dict and weakref appear to be very special cases within the Python system and as there is no general facility to replicate them working within the system does not appear to be viable. Memory hacking approach ~~~~~~~~~~~~~~~~~~~~~~~ The last system we attempted to mess with the memory layout of the object during the creation phase to append our memory after Pythons. To do this we need to override the memory allocator to allocate the requested memory plus our extra. We can then access this appended memory by computing the correct size of the object and thus our slot is on the end. We can test if the slot is present by looking to see if both `tp_alloc` and `tp_finalize` point to our Java slot handlers. This means we are still effectively a slot as we can test and access with O(1). The downside of this approach is there are certain cases in which the type of an object can be changed during the destruction phase which means that our slot can point to the wrong space if the basesize is changed out from under us. To guard against this we need to close our type system by imposing a ClassMeta which closes off mixin types that do not inherit from one of the special case base classes we have defined. The API implications should be small. There was never a functional case where extending a Java object within Python actually made sense as the Python portion is just lost when passed to Java and unlike Proxies there is no way to retrieve it. Further the extending a Java object within Python does not bind the lifespan of the objects so any code that used this is likely already buggy. We will properly support this option with `@JExtends` at a latter point. With this limitiation in mind, this appears to be the best implementation - It adds the slot to all of the required types. - The slot is immediately accessable using just two fields (basesize, itemsize) - The slot can be tested for easily (check tp_alloc, tp_finalize) - It closes the type system by forcing a meta class that guards against inappropraite class constuction. We could in principle add the slot to the "front" of the Python object but that could cause additional issues as we would require also require overriding the deallocation slot to disappear our memory from the free. The Python GC module has already reserved the memory in the front of the object so the back is the next best option. Speed patch implications ------------------------ Other than improving the speed, the speed patch has a lot of below the hood changes. So long as the user was not accessing private members there is no API change, but everything below that is gone. All private symbols like `__javaclass__` and `__javavalue__` as well as all exposed private members vanish from the interface. There is no longer a distinction between class wrappers and `java.lang.Class` instances for purposes of the casting system. The wrapper is an extension of a Python type and has the class methods and fields, and the instance is an extension of a Python object without these. Both hold Java slots to the same object. Therefore a lot of complexity of the private portions is effectively removed from the user view. Every path now has the same resolution, check the Java slot first and if not assume it is Python. Two private methods now appear on the wrapper (though I may be able to hide them from the user view.) These are the test entry points `_canConvertToJava` and `_convertToJava`. Thus the speed patch should be transparent all user code that does not access our private members. That said some code like the Database wrappers using JPype have roots in some code that did make access to the private tables. I have sent corrections when we upgraded to 0.7 series thus making them conforming enough not to touch the private members. But that does mean some modules may be making such accesses out in the wild. The good new is after the speed patch pretty much everything that is supposed to be under the hood is now out of the reach of the user. Thus the chance of future API breakage is much lower. Below the hood changes ====================== - We have tried to prevent backend changes from reaching the API, though this is not always entirely the case. The majority of the cases not already noted appear to be in the range of implementation side effects. The old implementation had bugs that create undefined behaviors like reinterpet casting buffers and the like. It is not possible to both fix the bugs in the backend and make preserve buggy behavours on the front end. We have limited our changes to only those for which we can see no desirable use existing. Calling a list slice assignment on a float from a numpy array of ints and getting a pile of gibberish was as far as we can tell not useful to the user. - setResource is dropped in favor of caching the module dictionary once at start of the JVM. We have a lot of resources we will need and the setResources method was cumbersome. - There is a lot of thrashing for the Python module style between C and C++ style. The determining blow was that C++ exception warning showed up when the proper linkage was given. Thus the perferred style flipped from C++ style to C. Thus the naming style change accordingly. - In addition to the style change there is also an attempt to isolate symbols between the different classes. The older style with a formal header that declares all the symbols at the top encouraged access to the functions and increased the complexity. Moving to a C style and making everything static forces the classes to be much more independent. - With the change to C style there is natural split in CPython class files between the structure declaration, static methods that implement Python API functions, the declaration of the type, and the exposed C++ style API used by the rest of the module. - There is some thrashing on how much of the C++ wrapper style Python API to keep. The rewrapping of the API was mainly so support differences between Python 2.7 and 3.x. So we dropped where we could. Only the JPPyObject which acts as memory handling device over pure Python style (because Python style is not exception safe) is strongly needed. - There is some spacing thrashing between different editors and the continuing debate of why C was written to have the pointer stick to to the variable rather than the type. When writing `Object* foo` it implies that the star is stuck to type rather than the variable where C reads it as the opposite. Hence there is the endless churn between what is correct `Object *foo` and what we would say in which `Object*` is actually a type. As Python favors the former and we currently have the latter that means at some point we should just have formatter force consistency. - We introduced Py_IsInstanceSingle. Is is a missing Python API function first for fast type check method of a single inherited type using the mro. Then something is singlely inherited we can bypass the whole pile of redirects and list searchs and just compare if the mro list matches at the same point counted from the end. As all of our base types are single inherited this saves time and dodges the more expensive calls that would trigger due to the PyJPClassMeta overrides. - We introduced Py_GetAttrDescriptor. This was previously implemented in Python and was very slow. Python has very good implementations for GetAttr but unfortunately it has the behavior that it always dereferences descriptors. Thus it is useless in the case that we need to get the descriptor from the type tree. We have reimplemented the Python type search without the descriptor dereferencing behavior. It is not nearly as complete as the Python method as we only need to consult the dictionary and not handle every edge case. That of course means that we are much faster. - As with every rewrite there is the need to cleanup. Anything that wasn't reached by the test bench or was identified as being called only from one location was removed or reencorperated back in into the function that call it. jpype-1.5.0/doc/ChangeLog-0.7.rst000066400000000000000000000546141453713375400163220ustar00rootroot00000000000000:orphan: JPype 0.7 Core ChangeLog ======================== Here is the "complete" log of the changes I think I made. Module changes -------------- * Moved Python module objects to namespace ``PyJP`` so that they are consistent with a Python module namespace. Renamed module classes presented to ``_jpype`` extension to ``PyJP*`` to match the internal classes. Though not exactly a standard convention, the types were internal anyway and having the names match the C structure makes it more clear what resource is being accessed. It also eliminates the confusion between ``jpype`` and ``_jpype`` resources. * Removed all usage of Capsule from the extension module. This was bridging between Python versions and had to be replicated on old platforms. As the capsules were functioning as crippled objects, they could not have methods of their own. Thus functionality that properly belonged to a specific class would get pushed to the base class. This affected former capsules of ``JPObject``, ``JPProxy``, and ``JPArray``. These are now formal classes in the module as ``PyJPValue``, ``PyJPProxy``, and ``PyJPArray``. * Moved the initialization of each class to the ``__init__`` function. Thus rather than creating the resource at the top level ``_jpype`` module (such as ``_jpype.findClass('cls'))``, the resource is created by allocating a new object (such as ``_jpype.PyJPClass('cls')``). * The presentation of ``JPArrayClass`` has been merged as a generic ``JPClass``. The only requirement for creation of an array instance is that the supplied ``PyJPClass`` satisfy ``isArray()``. * Removed direct dependencies that objects holding resource be exactly the type in ``jpype`` module. This reduces the restrictions in the underlying Python layer and allows for multiple classes such as ``JavaArray``, ``JWrapper``, and ``JavaClass`` to all be recognized as holding resources. This simplifies some paths in the ``jpype`` module where we needed to simply access a single method during bootstrapping and we were forced to construct complete classes necessitating the order of resource loading. * Remove ``JPObject`` concept and replaced it with ``JPValue``. ``JPValue`` holds the type of the object and a ``jvalue`` union. Both ``JavaClass`` and ``JWrapper`` now point to these classes as ``__javavalue__``. Anything with a ``__javavalue__`` with type ``_jpype.PyJPValue`` is now recognized as being a Java object. * Changed the recognization of a ``JavaClass`` to any object holding ``__javaclass__`` with type ``_jpype.PyJPClass``. This allows array classes, object classes, and wrappers classes to be used together. * Added hooks to direct convert ``PyJPClass`` to a ``PyJPValue`` with a type of ``java.lang.class`` and an object to the class. This replaces the need for calling ``forName`` to get to the existing class. * Changed ``PyJPField`` and ``PyJPMethod`` to descriptors so that we do not need to mess with ``__getattribute__`` and ``__setattr__`` in many places. * Eliminated the unnecessary class bound method. C++ Reorg ~~~~~~~~~ * Reorganized the type tree in the C++ layer to better match the Java structures. * Flattened out the redundant layers so that ``JPType`` is now ``JPClass`` corresponding to an instance of a ``jclass``. * ``JPClass`` is not the base class. Arrays are now objects and have base classes and methods. * Split ``JPClass`` into a separate type for each specialized object class for boxed, ``java.lang.Object``, and ``java.lang.Class`` which all required specialized conversion rules. * Boxed, string, base ``java.lang.Object`` and base ``java.lang.Class`` are now specialized with their required conversion rules. Path reduction ~~~~~~~~~~~~~~ * Removed ``HostRef`` and all of its usage. It was a halfway memory management method. To be passed around it was being held as a dynamically allocated pointer type with no static presence for cleanup. This defeats the point of having a smart point wrapper if the smart pointer is being used as a pointer itself. Thus it was only as safe as the user applied conventions rather than safe by design. * Replaced all the ``HostRef`` methods and ``JPy*`` Python object wrappers with a new smart pointer concept (namespace ``JPPy``). This removes the redundant host and ``JPy*`` wrapper layers. * Removed multiple optimization paths such as bypassing between ``jchar`` and ``unicode`` if the size matched. These paths were for speed reasons, but they could only be tested on particular machines. Thus it was difficult to tell if something was broken. It is better to have one tested code path that is slight slower, then a faster path that is busted. * Removed dead class ``JPCharString``. * **(bug)** Replaced all string handling with conversion through UTF8. Java and Python use different UTF8 encodings and thus those paths that were trying to short cut directly through from one system to another were badly flawed. By forcing a conversion to and from each time a Java string or Python string are passed eliminates conversion problems. This should resolve user issues having to do with truncating extended unicode characters. * Combined all code paths in ``canConvertToJava`` and ``convertToJava`` to use the ``JPValue`` * Combined code paths from ``check`` and ``get`` for ``JPValue``, ``JPClass`` and ``JPProxy`` ``get`` patterns when fetching from Python. Almost always we want to use the object immediately and just check if we can. * Removed the entirely redundant Primitive type ``setRange`` and ``getRange``. That code was entirely dead because it could not be reached. Renamed the direct methods as they now have the same function. * Removed ``JPTypeName``. This concept will be phased out to support lambdas. ``TypeManager`` now used ``getCanonicalName()``. Transferred responsibility for conversion to native names to Python module interface. * Introduced named classes for all specialized instances of classes to be held in ``TypeManager`` namespace. Thus converted most of the "is this type" to comparison of ``JPClass*`` pointers in place of string level comparisons. * Removed near duplicate methods. ``JProxy`` was requesting slightly altered copies of many conversions to support its usage. These operations could be supported by just splitting to two existing methods. Thus we could eliminate a lot of stray methods that served this specialized purpose. * ``JPArray`` is now a method holder rather than the primary object like ``JPBoundMethod``. All array objects in Python now hold both a ``__javaarray__`` and a ``__javavalue__``. This eliminates need for special paths for arrays. * ``_getClassFor`` is now overloaded to work with array classes. Thus asking for a ``JClass('[java.lang.Object;')`` will now correctly return a JavaArrayClass. * Constructing a string now shortcuts to avoid methodoverload resolution on new instance if given a Python string. * Reworked the GIL handling. The previous model was doing all the release locks on the JPJni calls automatically for almost all jni transactions. This would be fine, except that many utility functions were using those same calls regardless of whether is was a good time to release the lock. This ultra fine grain locking was effectively allowing any call to JPJni methods to become a break point, including those calls in critical sections such as ensureTypeCache and TypeManager::findClass. Any time it loaded a class or looked up a name it could be interrupted and thus end up in a corrupt state. Thus I moved all of the GIL calls to those places where we call user code on the type returns and the object constructors. Thus cuts the number of GIL transactions greatly and eliminates the need to deal with trampling global resources. The refactor exposed this a bit more because the removal of TypeName meant that we did a lot more transactions to get the class name. But that does not mean the flaw was not there before. If our tests cases had been any more aggressive about creating class instances during execution it would have overrun the TypeManager table and all would have failed. * Removed the previous default option to automatically convert ``java.lang.String`` to either a Python string or a unicode when returning from Java. This does mean some string operations now require calling the Java string method rather than the Python one. Having strings not convert but rather remain on the jvm until needed cuts the conversion costs when working with Java heavy code. I added a caching mech so that if we need to convert the string multiple times, we don't pay additional over the previous option. * A special ``toString`` method was added to ``PyJPValue`` to convert Java strings to Python strings. This can convert Java string resources to Python ones on request. Proxy changes ~~~~~~~~~~~~~ * Proxy as implemented previously held only a pointer to the proxy object and from this proxy object it lookup up the callable using either a dictionary or an instance. The majority of the resources were held by the ``jpype.Proxy``. This was replaced with a more general function in which the ``PyJPProxy`` proxy holds two resources. One is an object instance and the other is a lookup function that turns the name to a function definition. This supports the same use cases but eliminates the need for finding resources by convention. There is no need for the proxy in Python to have any specific layout other than holding a PyJPProxy as ``__javaproxy__``. Thus allowing alternive structures such as Proxy by inheritance to work. * Memory handling was changes slightly as a result so that the reference queue is now responsible for cleaning up the proxy. Proxy handle instances are generated whenever the proxy is passed to Java. Thus we form no counting loops as the proxy has no reference to the handles and the handles hold a reference to the proxy. Exception changes ~~~~~~~~~~~~~~~~~ * Changed all exception paths to use ``JPypeException`` exclusively. The prior system did way to much in the Exception constructors and would themselves crash if anything unusual happened making changing of the system nearly prohibitive to debug. Everything bubbles down to ``toJava`` and ``toPython`` where we perform all the logging and pass the exception off. This also centralizes all the handling to one place. * This pulls all the logic from ``JPProxy`` so that we can now reuse that when returning to any Java jni native implemented function. * Same thing for Python, but that was already centralized on ``rethrow``. * Reworked exception macros to include more info and introduced ``JPStackInfo``. It may be possible to connect all the stack info into the Python traceback (via a proxy class) to present a more unified error reporting. But this work is currently incomplete without a Python layer support class. * Integrated ``JPStackInfo`` into tracer to give more complete logs when debugging. Code quality ~~~~~~~~~~~~ * Applied a source formatter in netbeans. It is not perfect as it tends to add some extra spaces, but it does make faster work of the refactor. Custom spacing rules were applied to netbeans to try to minimize the total changes in the source. * Improved error handling where possible. * Rework ``JPTracer`` so that reporting from places that do not have a formal frame or could not properly throw (such as destructors) and still appear in the trace log. All ``TRACE`` macros were moved to ``JP_`` so that were less likely to hit conflicts. Removed guards that complete disabled Tracer from compiling when ``TRACE`` was not enabled so that unconditional logging for serious failure such as suppressed exceptions in destructors can report. * Defensively added ``TRACE`` statements whenever entering the module for a nontrivial action so that errors could be located more quickly. * Removed ``MTRACE`` layer as Java local frame handles all cleaning tasks for that now. * Replaced TRACE1, TRACE2, TRACE3 with a variodic argument macro ``JP_TRACE`` because I am too lazy to remember to count. * Renamed functions to best match the documented corresponding function in the language it was taken from. Thus making it easier to find the needed documentation. (Ie ``JPyString::isString()`` becomes ``JPPyString::check()`` if the corresponding language concept is ``PyString_Check()``). This does mean that naming is mixed for the Java/Python layers but it is better to be able to get the documentation than be a naming idealist. * Used javadoc comments on header of base clases. These strings are picked up by netbeans for document critical usage. * Moved method implementations and destructors out of headers except in the case of a truly trivial accessor. This has a small performance loss because of removal of inline option. This reduces the number of redundant implementation copies at link time and ensures the virtual destructor is fixed in a specific object. We can push those back to the header if there is a compelling need. ``jpype`` module changes --------------------------- Because these do affect the end user, we have marked them as enhance, change, remove, bug fix, or internal. General ~~~~~~~ * **(enhance)** ``__all__`` added to all modules so that we have a well defined export rather that leaking symbols everywhere. Eliminated stray imports in the jpype namespace. * **(enhance)** Add ``@deprecated`` to ``_core`` and marked all functions that are no longer used appropraitely. Use ``-Wd`` to see deprecated function warnings. * **(enhance)** Exposed ``JavaInterface``, ``JavaObject``, ``JavaClass`` so that they can be used in ``issubclass`` and ``isinstance`` statement. ``JavaClass.__new__`` method was pushed to factory to make it safe for external use. * **(enhance)** mro for Java Classes removes ``JavaInterface`` so that ``issubclass(cls, JavaInterface)`` is only true if the class not derived from ``JavaObject``. * **(enhance)** All classes derived from ``java.lang.Throwable`` are now usable as thrown exceptions. No requirement to access special inner classes with exception types. Exceptions can be raised directly from within a Python context to be passed to Java when in proxy. Throwables now use a standard customizer to set their base class to the Python Exception tree. Deprecated ``JException`` * **(enhance)** ``args`` is a property of ``java.lang.Throwable`` containing the message and the cause if specified. * **(enhance)** ``JChar`` array now converts to a string and compares with string properly. Conversion uses range so that it does not try to convert character by character. * **(remove)** ``JByte`` array is not a string type. It is not a string in Java and should not be treated as a string without explicit conversion. Conversion path was horribly inefficient converting each byte as a Python object. Test marked as skip. * **(change)** Array conversion errors produce ``TypeError`` rather than ``RunTimeError``. * **(enhance)** ``JArray`` now supports using raw Python types as the specifier for array types. It will convert to the most appropraite type or return an error. * **(remove)** property conversion customizer is deactivated by default. This one proved very problematic. It overrided certain customizers, hid intentionally exposed fields, bloated the dictionary tables, and interferred with the unwrapping of exception types. We can try to make it an optional system with ``import jpype.properties`` or some such but it will still have all those problems. Best to kill this misfeature now. * **(enhance)** ``JArray`` classes now have ``class_``. We can access the component type. This makes them more consistent with ``JClass``. (required for testing) * **(enhance)** Use of constructor call pattern eliminated the need for use of a separate factory and type. Thus we are back to the original design in which we only need to expose a small number of "types". This was applied to ``JArray``, ``JClass``, ``JException``, and ``JObject``. Use of ``isinstance()`` and ``issubclass`` now supported. The only challenge was keeping box types working. * **(remove)** Functions that return a string now return a ``java.lang.String`` rather than converting to Python. Thus when chaining elements together in Java will get the full benefit matching types. The previous auto convert has been removed. * **(enhance)** ``java.lang.String`` now has much more complete set of Python operations. String conversions are now cached, so the penalty of converting is kept to a minimum. Wrappers ~~~~~~~~~ * **(internal)** Rewrote the ``JWrapper`` module from scratch to reflect the use i of ``JPValue``. Renamed ``_jwrapper`` to ``_jtypes``. The concept of wrappers has now been lost internally. All objects and primitives are just values. * **(enhance)** Created import module containing all of the symbols needed for creating types in jpype so that we can support a limited import statement ``from jpype.types import *`` * **(enhance)** ``JString`` contructor now returns a ``java.lang.String`` object. Removed ``JStringWrapper`` as ``java.lang.String`` serves its purpose. * **(enhance)** ``JObject`` now returns an object with the Java type as a functional object rather than a dead end wrapper. This does allow some redundant things such as converting a Python class wrapper into a class ``JObject(java.lang.String) == java.lang.String.class_`` but otherwise seems good. * **(enhance)** 'JObject' and 'JString' accept 0 arguments to generate a generic object and empty string. * Tried to be more consistent about returning errors that are valid in Python. - Too many or two few arguments to a function will throw a ``TypeError`` - Value conversion out of range will throw ``OverFlowError`` - Value conversions that are the right type but invalid value will give ``ValueError`` (char from string too long) - Type conversions that cannot be completed should give ``TypeError``. - Errors setting attributes should give ``AttributeError`` such as trying to set a final field or trying to get an instance field from a static object. - Arrays access should produce ``IndexError`` on bad range. (it would be nice if these also mapped to Java errors and the corresponding errors in Java were derived from the Python error so that we can properly look for ArrayIndexOutOfBoundsException (derived from IndexException). But that is too heavy to attempt now.) * **(enhance)** ``JArray``, ``JException`` and ``JObject`` report as JavaClass when using issubclass. * **(enhance)** Short cut for just adding a base class as a customizer. Internal ~~~~~~~~~ * **(internal)** Changes corresponding to the ``__init__`` rework to match revised ``PyJP*`` classes. * **(internal)** Changes corresponding to the capsule removal. * **(internal)** Remove ``SPECIAL_CONSTRUCTOR_KEY`` as everything that uses it can recognize a PyJPValue as indicating they are receiving an existing Java resource as input. All special handling required to construct objects from within C++ layer were thus eliminated. * **(internal)** Removed almost all required resources from Python needing to be register in ``_jpype`` with the exception of getClassMethod. * **(internal)** Java class customizers did not need to be deferred until after the JVM is initialized. Pushing them into the dictionary immediately fixes issues in which a customizer was not applied to classes during early bootstrapping. This eliminates a large number of the need for calling initialize on each jpype module in ``_core``. * **(internal)** ``JArrayClass`` and ``JClass`` are the same for purposes of Customizers and class tree. * **(internal)** Customizer code and dictionary moved to ``_jcustomizer`` so that i it can be shared between Object and Array classes. * **(internal)** Converted ``JavaClass`` to more Python like "try first, eat an exception if it fails" philosophy to increase robustness to failure. This eliminates the problems when a new base class is introduced with a customizer without setting up a meta class. * **internal/enhance** Broke connections between boxed types and wrappers. User supplied wrappers can implements specified "Value" method. Wrapper types now have similar methods to boxed types with appropriate range checks. * **(internal)** All ``$Static`` meta classes have been eliminated. There is now only one tree of classes. A single meta class ``JClass`` serves as the type for all classes. Bugs ~~~~~~~ * **(bug fix)** Fixed bug in ``jpype.imports`` in which it would not install its hooks if loaded afer the jvm was started. * **(bug fix)** Fixed bug in JBoxed type wrappers in Python which would lead ``java.lang.Double`` and ``java.lang.Float`` to have an integer value when boxed was corrected. * **(bug fix)** Fixed bug in ``JObject`` that was preventing classes from being wrapped as objects. Verified a number of test cases in the test suite. * **(bug fix)** Reenabled the throw from Java test during proxy. The issue was that jpype was releasing resources before it could transfer control a ``PyErr_Clear`` removed the reference and thus our throwable was invalid. It was dastardly to find, but the fix was moving a statement one line up. Documentation changes ~~~~~~~~~~~~~~~~~~~~~~ * Documentation of major class methods have been added as well as marker whereever the underlying assumptions are not reasonably transparent. * Action items for further work have been marked as FIXME for now. Incomplete ---------- These tasks had to be pushed over post 0.7 release. * Finish specialization of ``JPArray`` classes for ``byte[]`` and ``char[]`` * Deal with fast array conversions misuse of types. ``int[]<=>float[]`` * Direct bridge methods for ``char[]`` are currently bypassing the unicode translation layer. It is unclear what Java does with extended unicode when dealing with ``char[]``. * Add a system to register a translation customizer so that we do not need to modify C++ code to add new simple translations like Python date to Java Instant. These would be installed into the PyJPClass during class wrapper customization. We will need to make sure each class has a Python type wrapper cached in ensureTypeCache so we are guaranteed to find the conversion. * Add tests for Exception.args jpype-1.5.0/doc/Makefile000066400000000000000000000151461453713375400150740ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JPype.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JPype.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/JPype" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JPype" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." jpype-1.5.0/doc/_static/000077500000000000000000000000001453713375400150535ustar00rootroot00000000000000jpype-1.5.0/doc/_static/theme_overrides.css000066400000000000000000000002411453713375400207460ustar00rootroot00000000000000/* override table width restrictions */ @media screen and (min-width: 767px) { .wy-table-responsive table td { white-space: normal !important; } } jpype-1.5.0/doc/android.rst000066400000000000000000000064351453713375400156070ustar00rootroot00000000000000JPype for Android ================= We ported JPype to the Android system. There are a number of important differences between JPype on Android and on a JVM. The Andoid platform uses the Dalvik virtual machine(DVM) which supports only a portion of the Java specification. As a result some portions of JPype have been removed or function differently. Supported Functions ------------------- The following features operate the same on both the JVM and DVM version. - Java classes using ``jpype.JClass`` - Access to Java methods and fields - Java arrays using ``jpype.JArray`` including slicing and direct transfers - Java primitive types, string type, and boxed types - Java.nio byte buffer including memory mapped data - Python collections API for Java collection types - Class customizers and type conversions - Support of scientific codes such as numpy - Implementation of Java interfaces using JProxy Functional differences ---------------------- When using JPype on Android, the virtual machine is started prior to the start of Python. Thus, there is no need to lauch the machine. Instead to use jpype only the statement ``import jpype`` is required. In addtion, the following functions have been removed... - ``jpype.startJVM`` is removed as the virtual machine cannot be started more than once. - ``jpype.shutdownJVM`` is removed as DVM cannot to stopped during operation. - ``jpype.addClassPath`` is removed as DVM does not support class path based jar loading. Dex files can be loaded dynamically using the Android API. - ``jpype.getDefaultJVMPath`` is removed as there is no JVM on Android. - ``jpype.beans`` has been removed as adding addition properties for semantic sugar increase the memory profile unnecessarily. - attach and detaching of threads is not allowed and those entry points have been removed. Removed JPype Services ---------------------- Not all JPype services are provided on Android as some functions depend on the internals of the JVM. These include... - The dynamic class loader does not operate on JPype as DVM does not support jar files. - jpype.imports and jpype.JPackage use the jar file system to identify packages which is not available on DVM. Therefore, all classes must be loaded use ``jpype.JClass``. - jpype support for Javadoc was removed as DVM does not support Javadoc jars in the classpath. - jpype does not install interrupt handlers for ^C on Android. Unsupported Java libraries -------------------------- Some Java libraries are not supported on Android. - Bytecode manipulation libraries to not function on DVM. This means generation of dynamic classes, manipulation of loaded classes, and Java agent code will not function. - Some standard Java libraries are not implemented on Android such as AWT. Jar libraries that access these unimplemented libraries will not function. - Private JVM internal classes such as ``sun.*`` are not implemented and cannot be used. Behavior differences -------------------- Some changes in JPype behavior occur simply because the DVM operates differently. These changes include - java.lang.Class.forName will not load classes from classes.dex. Attempting to access classes with forName will get ClassNotFoundException. Applying forName to base classes that use the system classloader function as expected. jpype-1.5.0/doc/api.rst000066400000000000000000000061071453713375400147340ustar00rootroot00000000000000API Reference ============= JVM Functions ~~~~~~~~~~~~~ These functions control and start the JVM. .. autofunction:: jpype.startJVM .. autofunction:: jpype.shutdownJVM .. autofunction:: jpype.getDefaultJVMPath .. autofunction:: jpype.getClassPath .. autodecorator:: jpype.onJVMStart Class importing ~~~~~~~~~~~~~~~ JPype supports several styles of importing. The newer integrated style is provided by the imports_ module. The older ``JPackage`` method is available for accessing package trees with less error checking. Direct loading of Java classes can be made with JClass_. For convenience, the JPype module predefines the following ``JPackage`` instances for ``java`` and ``javax``. .. autoclass:: jpype.JPackage Class Factories ~~~~~~~~~~~~~~~ .. _JClass: .. autoclass:: jpype.JClass .. autoclass:: jpype.JArray .. autoclass:: jpype.JException Java Types ~~~~~~~~~~ JPype has types for each of the Java primitives: ``JBoolean``, ``JByte``, ``JShort``, ``JInt``, ``JLong``, ``JFloat`` and ``JDouble``. There is one class for working with Java objects, ``JObject``. This serves to cast to a specific object type. .. autoclass:: jpype.JObject .. _threading: Threading ~~~~~~~~~ .. autofunction:: jpype.synchronized .. autoclass:: java.lang.Thread :members: Decorators ~~~~~~~~~~~ JPype uses ordinary Python classes to implement functionality in Java. Adding these decorators to a Python class will mark them for use by JPype to interact with Java classes. .. autodecorator:: jpype.JConversion .. autodecorator:: jpype.JImplementationFor .. autodecorator:: jpype.JImplements .. autodecorator:: jpype.JOverride Proxies ~~~~~~~ JPype can implement Java interfaces either by using decorators or by manually creating a JProxy. Java only support proxying interfaces, thus we cannot extend an existing Java class. .. autoclass:: jpype.JProxy Customized Classes ~~~~~~~~~~~~~~~~~~ JPype provides standard customizers for Java interfaces so that Java objects have syntax matching the corresponding Python objects. The customizers are automatically bound to the class on creation without user intervention. We are documentating the functions that each customizer adds here. Information about Java methods can be found in the Javadoc. These internal classes can be used as example of how to implement your own customizers for Java classes. .. autoclass:: java.util.Iterable :members: :special-members: .. autoclass:: java.util.Collection :members: :special-members: .. autoclass:: java.util.List :members: :special-members: .. autoclass:: java.util.Map :members: :special-members: .. autoclass:: java.util.Set .. autoclass:: java.util.Iterator .. autoclass:: java.util.Enumeration .. autoclass:: java.lang.AutoCloseable Modules ~~~~~~~ Optional JPype behavior is stored in modules. These optional modules can be imported to add additional functionality. .. _imports: .. automodule:: jpype.imports .. automodule:: jpype.pickle .. autoclass:: jpype.pickle.JPickler .. autoclass:: jpype.pickle.JUnpickler .. automodule:: jpype.beans .. automodule:: jpype.types jpype-1.5.0/doc/attach_debugger.png000077500000000000000000000412531453713375400172530ustar00rootroot00000000000000PNG  IHDREC4zAsRGBgAMA a pHYsttfxB@IDATx^|e_@J^Q "4+ XQ8EzNِ)`SDDQ&Ed&,!n* _3ݝ7{fcBwv!7ObbbܭPB^A ١\PBGv ') B#"VDRxС(ٳ'}[®]`nY\\[|ɒ[[|`*(֭.[HeʗbEP.۴+]ɝQ6֪}c;0#edhP )^&eӴѺ1;l ~g+'m3V%sء]m8d11(RZ[`E1 Uun$yD/PUkn* މJ}NTBvvO@E>[z"2©EFsիWKRן@l[nS$駝h?_jgdb٦ElíD>{[=VlhN? e~٘n:YÚ-Kܱ~C1fZJXk_|ت׮nvVmNV&!CI 64r^  :1vȶ_f*WJX5wX)[^=V?Z>ʕ8Ѫ5Wqsk[Aӷxwkqf[k-'[y[Ot/ x-ZUzx o_]{|wR|rСN x^}UW[?|)if@7߸΋.{kyvTNQNnRΤ{= > 9ܹ͚5-馛gqӁo=Nqӧ]|E'%f[lݻU^ +J_J.Ǝϙkv饗sf6mre˖Mc=fӟm[^\ҷo_W%=sֻwo. ?CٸqlڵE*Zn{@裏O){i<:Q7%npgǪL.uZjYV-]E8U>07o6xw9s9_JOzsڟcC;6U &nWk.6lX8cW/h֯J*%Bm۶={FoԨQ=:k ^*qٳ J{wpGի_|aڵs+Vtוl߾V\ϙ]uU^o 믿6-{ǭf͚dɒ~I] N:-]u%wyԥot]-> mTӿ@<%Q}el}yɒ%vA73ҥ˸$>q-IƌKՂpYgY~$ݝ;wiQ,}짟n@MR*t%u ԃXR.V&M,.6oɟgz2m47JZQu%eDR~/o.`4Pyu)(UU֭ZHlZ* Oӣ]-Dz]'Hl[ʕCCjQ׏S믿޵^A-k_|[qؿkٻ䒤mƌ7BFyT7^ hԍZwzl RyF-5l2wJ/ BSu]ZtSZ»> ҵ y-nW!_w1[L駟&Z MW۶m%֪RTN RTm; ̓Aůnuk0drˌ~V8"? T:>]J_}.ZjXb^p^;C'Ry4ۖŊd% އHke)Z Z-L-ڀG:ɝ8q=n^얥?yW_A(G̨b҅~RhUKlٮP#r/#ػ*?uʣٶ*OX. (P79^S^\H4KI,X~R'anb(ʹmE+|z/.&S%Ot9e)Fe~KWF*mwVЅ>D#9rK44(0 tdD3dW?@Q/r3+AQFRR%~m!)*U6nJ+[ RS2j$JQKMZm[NPW^Z%Rv-TH`P@K&_~ *D*zԢش}Qy LtTffxP4bGyF-*]cHT1-Ms5<^ODdZwSX R9QrI;Y2(R0ūNzcT]VQ.xTgV %UCכ NT*Ful%2(hNHpPRrS$4|0kFϥ_w @4&Ol;wvilTPs>̆js=GrLf /w^-\^z%YvZʟ;[n^~ea=. Q9^5hѼ'gcѢEv5u쪫X4h|~i={ؕW^i{e˖֣GwGA#ݧg4AѶmK/on$TÆ mn#͛篕7_֬YK. >wm@v,}]@իW!Oqi=Wnx믿k_^R{6sҤI$onjԨ˔)cƍsӁo w)ifGo\~Ewy-nq[᝱L6~a馛'3gZ۳ʛԪ5b(ZWvjt׫W/XU\ٮz۾}_TYzɒ%iӦ~ɑvoyٖ-[{E)S 'Ow-?::CVd ǥ\j9Lj\g͚K4IkuYG<6{z_D]:2=V'=Qw]wϏ@u 7`>w-̩Ih u"moJi;2}]r%o/_-B_|; 褫gϞ\[6 >ܵ0yGC8#?slE{[|NwC^%jyp[ti誫 +W.TBP=cr=䓡x TjПɓ._B]taTRꆼpr_PJBŊ vinC=c GVVrU믿ꠌ{lC_īlC;v -\ݼ_[n~i(ԩSPڵC&L%&&~g$:sB-㼃_DC;w ˡƍvrW{r燼3G7ݢEw`wӒz?4w\725ӯ_Ђ B_~y.K_S/y5H6mw\VX;-?>3nP2eB ygt;Ie駟|J_훼 NM)YyXt ü+374Qt\3fLZaÆe֭swu p>rS;e((4iN>=/tvB~iZ_Рm۶.B p.V\/cÆ (Yfmܸ14sL|͚5nt֗{Æ ny4۪}h֭'&E*y@eڿ˖-s>_z$Gy\p cǏgvoj_͏92TpЦMܼԖDByM G|ܑTY }wk׮+Ww}\}?\޳;7n+{\嘖o^rHۛR4맶YyX_ӣ`Ea}+ܴ(({&MeC '4e>Sjr eLWR#fZzuSՍ$来fΝ؏?KU}\].][]Rw\ %6h=NU.Օ\2m ݨ:uXB\r5k_5mzkzLHHpLZh%VxQwu ;xAc bygU.)-ʿ)U˟;\F>J?4k̼nr^O4I>+ KMzEkh;:>_=;0"5Š+rS?D zNMZ7}F#)etp}J*TN8߿1cصzp^I{1)ELm ทRZ˳"CAe>5AYx~0uS悗/. QZ TTkժyg[e[F۪>ڗ_nsQ }ժUݧs_ne6cƌ[۶mt@_Tw6jfT{iȰG9p? NѼhU\!$IO/ddu\*d在RTUW]喧&>ш)ethdRn\vr1%}u >>S:]fM p.]O?%Ѽg Ekv[o/9Z@,^݋Z'$;O>kAٳ}$9y^ZtUR:+YZҚ5ofFtLU:t`_~ VjS #zlǎ]_p kRn=3p GA>S.4)(Xd $$2U̢נkkZCH~x ޗӧ3wJPeztiZoU*KԤ\'lGH+ZmZzaEK'J$}3@7|=n^얡Hzȝ)\]֗I&Ƹq{fΝZg*jժX)Aː_*Ks2mSTP7|7-+V .Pѭ[7+[ի_ύPS~+7rwƪ3SD|MEtps`o>0O=ރ[-V; te7xYƝjȑ#܈% SKާ2R[/uI]+6ѾhhDՋ/5tbB'=mPkmjlذar%R7vuIZ7/}FSrh7\4G)2B[rLI33ϸV cz4R'WW7g"ݔ0En$T蔈r{Kh@.ndN0+父|šN;4%z.\2|0IeAtFU7%(*1|Yn݂C7n)I6i߼^yz4&<9Fh2*ǥ\޽e5wB#2Xޗ߽EMH"mwjz)=F?i{S~4ۑRz+7j FUN@(ݧ%1㢣LPZXկo1psn%Zؙw`qgթo5z 9E]a9#{=+9Z:n%]tƯVشU5YY PE5Ey,(n꺞YE.=9]$@xYJƱ=n`tBՉCKQ>7kwBFA)*wYxq7RJV">>hSz>(h4h"E <x<E"ACP!(x<E~,ٸq?Sre*zE޽{jժHXb\>x#X(D- PNowvWX9drtMcIо}{w0`+9Eͳ~^y{Ǔu۷off.ZvZʟ˟?2=cϻ۹kjJ׭t=c#F{O5l)۷kE]дk.9rVNNZ_Æ ƍ[׮](5k{K[lKzYO>KͶmQY n;{)iӦٸqe˖6qDn:غu]z%VlYkڴ}~m/W_G}䗤o]:zyf~^zYŊuz۾}_TN:J,'5;u;sg=Slʔ)nyf^YAѺu駟vɓDVJ:d-\*V`/Uɿ{^[ɞ{Yv+\ST)$jI8?:gt{ 6؄ \oW&)o֭[JZCnvYnt^q$ڜ9s?ŋ%fwqmzi3f̰.]%i{ .tAW_}uD_=\F-Wk/5mԩ6j( )LI]]z5jȮJmT`;ݶKf^Y#.BWePBvEV8k}CXK(x']KA]%fEI&5k\$ꪫ\*[%C-MEQާOҠ"?U oZ9E˻iQZ8 <KC5\ {!%UиؤבN8>Sj֬WxQÆ MZW>U0zgjI*^RK~\VZق ܕ3Ȋ R^Z[hΝ\믿nv_nݺJ5|ýYf\Г$ QCr\P [»rL2scMMz_:CuyʕE^isOg}fcǎxɁCoVmڴK -[}T]]~Tѿ/Z'6{l_~ 6>s֥Kܹs;vt]Aў={"ǃ"tvQՅhKE +VpnSE{駻YQ?ڱt2㢛r ?65J VRr<,6x 7W<M]7p~ w7ښIyX ƛntyT ^+T(|111֛oj*uH̛oE-(i{5ޜzj#4c7<&Ie51+M V ^Z-=`'_사o6nJ+.\k5CCs`WVˎZ*U/9{lĈ_מycAѶm۬o߾V^=kРvmcl֬YveYZqƶegiӦٸqe˖6qD|ذaֿ7 ^z /`=UZ՚hnƬW5j&LeB7D'Ov-RH4i]ƪWnny%\E_hQ+_oM#G5ktAc||k)RygsiyѢqVX1PƹeLEjQu-Z1iӦ^lv!k֬ 0rDONPBV\9q=NU\/^ˢE}PVZ5?~ɦ1cƸBEuԱ&M^`;wt:j-̙3vf6Vf j5,Y!֭mLx#ArFmK-stS.ѨQ,ׯv(U(_ \s5ZE6uTLC䦑q7xY\.p| /\ppjE~7nh+VtAWlVR#q+J~ٲeS DPzD@tM#"F!(xNܹs)38ßA3ACP!(x<E"Ao# ,as˗/͛s 5g϶ڵksѣ CP!(d8O>[ZLL.]N?tQ_2h6k,:uNP(Zjn^M6mlȐ!n^?_xVzPBY.X9fd7Xַo_Avmَ;\W^jժe?\+Ƹqe˖6qDްaìniIٲe]Z@;֬ZesqY- RP~:u{m6lpi}뮹[\h^- իWɓ'֭[[]Rzj֭F)PQwW_/1XG (իWd*(UV֢E ;kժU.ϨO>V~}wS@gڵ#.B;N:VdIQhQ+_|rˇZڷo撚5 \=j(PKr^|E8-ZZ@~Is,>>>@ׁ`:rr/'|z/MÇ_m~zny͚5}Vo2ժU哄'ŋݽ(hܧnLuG4%[Ϻm 5K^@~D뀺Ú4ibog sN;vk-Nϟ%YbEr62;r˹hL-<=q_?emv3fw}ԅrl 4 {Ѷt2kԨ)H,e\s5ZE7v!#F4RgwLvIݺu]s)S?Y*UoJ zb[sαF Z=Ҭȯk$]TT)IdjP%5nҖݿ.6 cccec!)ʑ9#"G"gܹs)gFx>x<E"ACP!(x~8|rk޼?R3{l]?=<E8`wyuyOr}۽{?w+¾kT5i_\|Ů/}6n8i 6^z%KHH-[K3""˕y?n=۷yoG-ׯ5ksi{5 {Ǿ+5}C٬Y?}={tO"ںuZjYV-m~W5hn6۱c_j֣G7|򗿸gyƦMZZli'Nt#+P7n8H]jAѺu\O?mgYѢE+ɓ'[oeڵ!CkJ.cϷ>/I*۰aM0>#ܷr_jj*۹s?g.`Zz[]O>7?ֵkW[b?JKv͟R>SԽkիW3}XMk׺ǧD炚˻hERH4i]ƭ`*-jRB78r%(R(T)]KLL y睮UV.&hiڴ`:()n >:r `~ r%('~Oo\+[x"IP&P /TVƏo~_3f_<)9Yy6>lrў={ܵ}ԩ}hh^CcǎuE 빌s(zĈny@UI3gt~a_Kd!5Aw}ꫯڲeuG93~x{{7|$4|.oa ,rG^=c׿Նjm/ٱWrfРA{s|sy[vcڵ}vܹߊ+fʕs{6mlڵZIĉ9!Cfܑ͚o[okܸmܸ~aql+;A P믿?>o]Y~}J&LzY||Q˚Z3ΝD=3?[ 6"[lݻ1rM2/9Z^矷*Vhs?8_+v[O&]+>,Y/MxbkwޱڵkK 9F*T^> oWܬYϩʟx ^uYG%ꦛnr (Y5m/X"( ;o`o)ʖ-kW;hZG;h k׮p v! t_rX׮]ݽZ= ."rK _~ݷjݧE͋/h֯J*>ܼy}Vre{'RڦNjFrҸq}%"Ƞ-؜9s[|M<.wa9UDJUFZtN;ȨBO- Sk%-%H̜9Ӗ4 `(0I_nM48k۶QAjԵ?<3.L-FQñBPnÇq j*w?dBK.uS~%^%#$n疠뫯r 6LZ2T҂)H0_rOZnm͚5s-)ׯO%}f39p>+E & 3f$t ՕSJÔhir;<[bW)S^uєwAdF|jY>&"fuԱ;V,Y?+!MFu=zhWudH". >&Uď>V^z%k޼;RKA08SE9 ֭Zޔߦd46~=?3߇crرc]˅-T)~N:]8qq.עZj6l0{wd>[GVV-zk<#vkNw_S *M>}EF5zL 'l#Fե}7u*T%PGCy-ZpK ߗ\ñï8,\ +a&F}6`[~U߸RTz_;AK~4/ʫZܵk5l_rZhEGPFkV{lj?\!c_Rฒ*G],0#@ND]XkCɫ}"QkO e$ YZCFd>"(p\ш%*']w]vrMHȏ:7sOvk"ɜ.Ԙ[?}]?uunb_E+JD7")B~Qv`_}{{_h ย۴iy7()By!JU>G :;2TGc_瞜EK:ؿih EdKHܓCNCP!(x<E"ACP!(*Z:IENDB`jpype-1.5.0/doc/caller_sensitive.rst000066400000000000000000000101331453713375400175100ustar00rootroot00000000000000:orphan: Caller Sensitive Methods ======================== The following methods use the caller sensitive (as of JDK 12): - ``java.io.ObjectStreamClass.forClass`` - ``java.io.ObjectStreamField.getType`` - ``java.lang.Class.forName`` - ``java.lang.Class.newInstance`` - ``java.lang.Class.getClassLoader`` - ``java.lang.Class.getEnclosingMethod`` - ``java.lang.Class.getEnclosingConstructor`` - ``java.lang.Class.getDeclaringClass`` - ``java.lang.Class.getEnclosingClass`` - ``java.lang.Class.getClasses`` - ``java.lang.Class.getFields`` - ``java.lang.Class.getMethods`` - ``java.lang.Class.getConstructor`` - ``java.lang.Class.getConstructors`` - ``java.lang.Class.getField`` - ``java.lang.Class.getMethod`` - ``java.lang.Class.getDeclaredClasses`` - ``java.lang.Class.getDeclaredField`` - ``java.lang.Class.getDeclaredFields`` - ``java.lang.Class.getDeclaredMethod`` - ``java.lang.Class.getDeclaredMethods`` - ``java.lang.Class.getDeclaredConstructor`` - ``java.lang.Class.getDeclaredConstructors`` - ``java.lang.Class.getResource`` - ``java.lang.Class.getResourceAsStream`` - ``java.lang.Class.getNestHost`` - ``java.lang.Class.getNestMembers`` - ``java.lang.ClassLoader.getParent`` - ``java.lang.ClassLoader.getPlatformClassLoader`` - ``java.lang.invoke,MethodHandleProxies.asInterfaceInstance`` - ``java.lang.invoke.MethodHandles.lookup`` - ``java.lang.Module.addReads`` - ``java.lang.Module.addExports`` - ``java.lang.Module.addOpens`` - ``java.lang.Module.addUses`` - ``java.lang.Module.getResourceAsStream`` - ``java.lang.Package.getPackage`` - ``java.lang.Package.getPackages`` - ``java.lang.reflect.AccessibleObject.setAccessible`` - ``java.lang.reflect.AccessibleObject.setAccessible`` - ``java.lang.reflect.AccessibleObject.trySetAccessible`` - ``java.lang.reflect.AccessibleObject.canAccess`` - ``java.lang.reflect.Constructor.setAccessible`` - ``java.lang.reflect.Constructor.newInstance`` - ``java.lang.reflect.Field.setAccessible`` - ``java.lang.reflect.Field.get`` - ``java.lang.reflect.Field.getBoolean`` - ``java.lang.reflect.Field.getByte`` - ``java.lang.reflect.Field.getChar`` - ``java.lang.reflect.Field.getShort`` - ``java.lang.reflect.Field.getInt`` - ``java.lang.reflect.Field.getLong`` - ``java.lang.reflect.Field.getFloat`` - ``java.lang.reflect.Field.getDouble`` - ``java.lang.reflect.Field.set`` - ``java.lang.reflect.Field.setBoolean`` - ``java.lang.reflect.Field.setByte`` - ``java.lang.reflect.Field.setChar`` - ``java.lang.reflect.Field.setShort`` - ``java.lang.reflect.Field.setInt`` - ``java.lang.reflect.Field.setLong`` - ``java.lang.reflect.Field.setFloat`` - ``java.lang.reflect.Field.setDouble`` - ``java.lang.reflect.Method.setAccessible`` - ``java.lang.reflect.Method.invoke`` - ``java.lang.reflect.Proxy.getProxyClass`` - ``java.lang.reflect.Proxy.newProxyInstance`` - ``java.lang.reflect.Proxy.getInvocationHandler`` - ``java.lang.Runtime.load`` - ``java.lang.Runtime.loadLibrary`` - ``java.lang.StackWalker.walk`` - ``java.lang.StackWalker.forEach`` - ``java.lang.StackWalker.getCallerClass`` - ``java.lang.System.getLogger`` - ``java.lang.System.getLogger`` - ``java.lang.System.load`` - ``java.lang.System.loadLibrary`` - ``java.lang.Thread.getContextClassLoader`` - ``java.security.AccessController.doPrivileged`` - ``java.security.AccessController.doPrivilegedWithCombiner`` - ``java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater`` - ``java.util.concurrent.atomic.AtomicLongFieldUpdater.newUpdater`` - ``java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater`` - ``java.util.ResourceBundle.getBundle`` - ``java.util.ResourceBundle.clearCache`` - ``java.util.ServiceLoader.load`` - ``java.util.ServiceLoader.loadInstalled`` - ``java.util.logging.Logger.getLogger`` - ``java.util.logging.Logger.getLogger`` - ``java.util.logging.Logger.getAnonymousLogger`` - ``java.sql.DriverManager.getConnection`` - ``java.sql.DriverManager.getDriver`` - ``java.sql.DriverManager.deregisterDriver`` - ``java.sql.DriverManager.getDrivers`` jpype-1.5.0/doc/conf.py000066400000000000000000000261241453713375400147310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # JPype documentation build configuration file, created by # sphinx-quickstart on Wed Feb 26 20:16:40 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from unittest import mock import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.napoleon', 'sphinx.ext.autodoc', 'sphinx.ext.autosectionlabel', 'readthedocs_ext.readthedocs', ] autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'JPype' copyright = u'2014-18, Steve Menard, Luis Nell and others' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. class ObjectMock(object): def __init__(self, *args, **kwargs): self._kwargs = kwargs self._to = mock.Mock object.__init__(self) def __getattr__(self, key): kwargs = self._kwargs m = self._to(**kwargs) object.__setattr__(self, key, m) return m class TypeMock(type): def __new__(cls, name, bases=None, members={}, to=mock.Mock, **kwargs): if not bases: bases = tuple([]) members['__init__'] = ObjectMock.__init__ members['__getattr__'] = ObjectMock.__getattr__ members['_kwargs'] = kwargs members['_to'] = to members['__slots__'] = [] return type.__new__(cls, name, bases, members) def __init__(self, *args, **kwargs): return type.__init__(self, *args) def __getattr__(self, key): kwargs = self._kwargs m = self._to(**kwargs) type.__setattr__(self, key, m) return m class _JClass(type): pass class _JClassHints(object): def __init__(self): self.bases = [] def _addClassBases(self, *args): pass def _addTypeConversion(self, *args): pass def _addAttributeConversion(self, *args): pass def _excludeConversion(self, *args): pass class _JPackage: def __init__(self, *args, **kwargs): pass # Monkey patch (this is needed to catch errors for missing TypeMock) m = mock.Mock.__getattr__ def patch(self, key): if key == "__name__": return "FIXME TypeMock is missing" return m(self, key) mock.Mock.__getattr__ = patch mockModule = mock.MagicMock() mockModule.isStarted = mock.Mock(return_value=False) mockModule._JArray = TypeMock("_JArray") mockModule._JClass = _JClass mockModule._JField = TypeMock("_JField") mockModule._JMethod = TypeMock("_JMethod") mockModule._JObject = TypeMock("_JObject") mockModule._JProxy = TypeMock("_JProxy") mockModule._JException = TypeMock("_JException") mockModule._JPackage = _JPackage mockModule._JClassHints = _JClassHints mockModule._hasClass = lambda x: False sys.modules['_jpype'] = mockModule import jpype import jpype.imports # For some reason jpype.imports does not work if called in sphinx. Importing # it here solved the problem. version = jpype.__version__ # The full version, including alpha/beta/rc tags. release = jpype.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # otherwise, readthedocs.org uses their theme by default, so no need to specify it # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] html_css_files = ['theme_overrides.css'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'JPypedoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'JPype.tex', u'JPype Documentation', u'Steve Menard, Luis Nell and others', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'jpype', u'JPype Documentation', [u'Steve Menard, Luis Nell and others'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'JPype', u'JPype Documentation', u'Steve Menard, Luis Nell and others', 'JPype', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False napoleon_custom_sections = ["Static Methods", "Virtual Methods", "Constructors"] jpype-1.5.0/doc/dbapi2.rst000066400000000000000000000466671453713375400153430ustar00rootroot00000000000000################## JPype DBAPI2 Guide ################## `Introduction` ============== One common use of JPype is to provide access to databases used JDBC. The JDBC API is well established, very capable, and supports most databases. JPype can be used to access JDBC both directly or through the use of the Python DBAPI2 as layed (see PEP-0249_). Unfortunately, the Python API leaves a lot of behaviors undefined. The JPype dbapi2 module provides our implementation of this Python API. Normally the Python API has to deal with two different type systems, Python and SQL. When using JDBC, we have the added complexity that Java types are used to communicate with the driver. We have introduced concepts appropriate to handle this addition complexity. `Module Interface` ================== `Constructors` -------------- Access to the database is made available through connection objects. The module provides the following constructor for connections: .. _connect: .. autofunction:: jpype.dbapi2.connect Globals ------- JPype dbapi2 defines several globals that define the module behavior. These values are constants. .. _apilevel: `apilevel`_ The apilevel for the module is "``2.0``". .. _threadsafety: `threadsafety`_ The threadsafety level is 2 meaning "Threads may share the module and connections". But the actual threading level depends on the driver implementation that JDBC is connected to. Connections for many databases are synchronized so they can be shared, but threads must execute statement in series. Connections in the module are implemented in Python and have per object resources that cannot be shared. Attempting to use a connection with a thread other than the thread that created it will raise an ``Error``. Sharing in the above context means that two threads may use a resource without wrapping it using a mutex semaphore to implement resource locking. Note that you cannot always make external resources thread safe by managing access using a mutex: the resource may rely on global variables or other external sources that are beyond your control. .. _paramstyle: `paramstyle`_ The parameter style for JPype dbapi2 module is ``qmark`` ============ ============================================================== paramstyle Meaning ============ ============================================================== ``qmark`` Question mark style, e.g. ``...WHERE name=?`` ============ ============================================================== Exceptions ---------- The dbapi2 module exposes error information using the following exceptions: .. autoclass:: jpype.dbapi2.Warning .. autoclass:: jpype.dbapi2.Error .. autoclass:: jpype.dbapi2.InterfaceError .. autoclass:: jpype.dbapi2.DatabaseError .. autoclass:: jpype.dbapi2.DataError .. autoclass:: jpype.dbapi2.OperationalError .. autoclass:: jpype.dbapi2.IntegrityError .. autoclass:: jpype.dbapi2.InternalError .. autoclass:: jpype.dbapi2.ProgrammingError .. autoclass:: jpype.dbapi2.NotSupportedError Python exceptions are more fine grain than JDBC exceptions. Whereever possible we have redirected the Java exception to the nearest Python exception. However, there are cases in which the Java exception may appear. Those exceptions inherit from `:py:class:jpype.dbapi2.Error`. This is the exception inheritance layout:: Exception |__Warning |__Error |__InterfaceError |__java.sql.SQLError | |__java.sql.BatchUpdateException | |__java.sql.RowSetWarning | |__java.sql.SerialException | |__java.sql.SQLClientInfoException | |__java.sql.SQLNonTransientException | |__java.sql.SQLRecoverableException | |__java.sql.SQLTransientException | |__java.sql.SQLWarning | |__java.sql.SyncFactoryException | |__java.sql.SyncProviderException | |__DatabaseError |__DataError |__OperationalError |__IntegrityError |__InternalError |__ProgrammingError |__NotSupportedError Type Access =========== JPype dbapi2 provides two different maps which serve to convert data between Python and SQL types. When setting parameters and fetching results, Java types are used. The connection provides to maps for converting the types of parameters. An `adapter `_ is used to translate from a Python type into a Java type when setting a parameter. Once a result is produced, a `converter `_ can be used to translate the Java type back into a Python type. There are two lookup functions that select the behavior to decide how a column or parameter should be treated. These are `getters`_ and `setters`_. .. _adapters: adapters_ --------- Whenever a Python type is passed to a statement, it must first be converted to the appropriate Java type. This can be accomplished in a few ways. The user can manually convert to the correct type by constructing a Java object or applying the JPype casting operator. Some Java types have built in implicit conversions from the corresponding type. For all other conversions, an adapter. An adapter is defined as a type to convert from and a conversion function which takes a single argument that returns a Java object. The adapter maps are stored in the connection. The adapter map can be supplied when calling `connect`_ , or added to the map later through the `adapters `_ property. .. _setters: setters_ -------- A setter transfers the Java type into a SQL parameter. There are multiple types can an individual parameter may accept. The type of setter is determined by the JDBC type. Each individual JDBC type can have its own setter. Not every database supports the same setter. There is a default setter may that would work for most purposes. Setters can also be set individually using the ``types`` argument to the ``.execute*()`` methods. The setter is a function which processes the database metadata into a type. Setters can supplied as a map to `connect`_ or by accessing the `setter `_ property on a Connection. .. autofunction:: jpype.dbapi2.SETTERS_BY_META .. autofunction:: jpype.dbapi2.SETTERS_BY_TYPE .. _converters: converters_ ----------- When a result is fetched the database, it is returned as Jave type. This Java type then has a converter applied. Converters are stored in a map holding the type as key and a converter function that takes one argument and returns the desired type. The default converter map will convert all types to Python. This can be disabled by setting the converters to ``None``. The converter map can be passed in to the `connect`_ function, or set on the Connection using the `converters `_ property. It can be supplied as a list or a map to the ``.fetch*()`` methods. .. _getters: getters_ -------- JDBA provides more than one way to access data returned from a result. In the native JDBC, each executed statement returns a result set which acts as a cursor for the statement. It is possible to access each column using a different get method. The default map will attempt to fetch according to the most general type. The getter is a configurable function that uses the metadata to find the most appropriate type. .. autofunction:: jpype.dbapi2.GETTERS_BY_TYPE .. autofunction:: jpype.dbapi2.GETTERS_BY_NAME .. _Connection: `Connection Objects`_ ===================== A Connection object can be created using the using `connect`_ function. Once a connection is established the resulting Connection contains the following. .. autoclass:: jpype.dbapi2.Connection :members: .. _Cursor: `Cursor Objects`_ ================= These objects represent a database cursor, which is used to manage the context of a fetch operation. Cursors created from the same connection are not isolated, *i.e.*, any changes done to the database by a cursor are immediately visible by the other cursors. Cursors created from different connections may or may not be isolated, depending on how the transaction support is implemented (see also the connection's `rollback `_ and `commit `_ methods). .. autoclass:: jpype.dbapi2.Cursor :members: Cursors can act as an iterator. So to get the contents of table one could use code like: .. code-block:: python with connection.cursor() as cur: cur.execute("select * from table") for row in cur: print(row) `SQL Type Constructors` ======================= Many databases need to have the input in a particular format for binding to an operation's input parameters. For example, if an input is destined for a ``DATE`` column, then it must be bound to the database in a particular string format. Similar problems exist for "Row ID" columns or large binary items (e.g. blobs or ``RAW`` columns). This presents problems for Python since the parameters to the `.execute*()` method are untyped. When the database module sees a Python string object, it doesn't know if it should be bound as a simple `CHAR` column, as a raw `BINARY` item, or as a `DATE`. This is less of a problem in JPype dbapi2 than in a typically dbapi driver as we have strong typing backing the connection, but we are still required to supply methods to construct individual SQL types. These functions are: .. autofunction:: jpype.dbapi2.Date .. autofunction:: jpype.dbapi2.Time .. autofunction:: jpype.dbapi2.Timestamp .. autofunction:: jpype.dbapi2.DateFromTicks .. autofunction:: jpype.dbapi2.TimeFromTicks .. autofunction:: jpype.dbapi2.TimestampFromTicks .. autofunction:: jpype.dbapi2.Binary For the most part these constructors are largely redundant as adapters can provide the same functionality and Java types can directly use to communicate type information. .. `JDBC Types` `JDBC Types`_ ============= In the Python DBAPI2, the SQL type system is normally reduced to a subset of the SQL types by mapping multiple types together for example ``STRING`` covers the types `STRING`, `CHAR`, `NCHAR` , `NVARCHAR` , `VARCHAR`, and `OTHER`. JPype dbapi2 supports both the recommended Python types and the fine grain JDBC types. Each type is represented by an object of type JBDCType. .. autoclass:: jpype.dbapi2.JDBCType :members: The following types are defined with the correspond Python grouping, the default setter, getter, and Python type. For types that support more than one type of getter, the special getter can be applied as the converter for the type. For example, the defaulf configuration has ``getter[BLOB] = BINARY.get``, to get the Blob type use ``getter[BLOB] = BLOB.get`` or specify it when calling `use `_. ======== ======================== =================== ============== ================= =============== Group JDBC Type Default Getter Default Setter PyTypes Special Getter ======== ======================== =================== ============== ================= =============== DATE DATE getDate setDate datetime.datetime DATETIME TIMESTAMP getTimestamp setTimestamp datetime.datetime TIME TIME getTime setTime datetime.datetime -------- ------------------------ ------------------- -------------- ----------------- --------------- DECIMAL DECIMAL getBigDecimal setBigDecimal decimal.Decimal DECIMAL NUMERIC getBigDecimal setBigDecimal decimal.Decimal -------- ------------------------ ------------------- -------------- ----------------- --------------- FLOAT FLOAT getDouble setDouble float FLOAT DOUBLE getDouble getDouble float FLOAT REAL getFloat setFloat float -------- ------------------------ ------------------- -------------- ----------------- --------------- NUMBER BOOLEAN getBoolean setBoolean bool NUMBER BIT getBoolean setBoolean bool NUMBER TINYINT (0..255) getShort setShort int NUMBER SMALLINT (-2^15..2^15) getShort getShort int NUMBER INTEGER (-2^31..2^31) getInt getInt int NUMBER BIGINT (-2^63..2^63) getLong getLong int -------- ------------------------ ------------------- -------------- ----------------- --------------- BINARY BINARY getBytes setBytes bytes BINARY BLOB getBytes setBytes bytes getBlob BINARY LONGVARBINARY getBytes setBytes bytes BINARY VARBINARY getBytes setBytes bytes -------- ------------------------ ------------------- -------------- ----------------- --------------- TEXT CLOB getString setString str getClob TEXT LONGNVARCHAR getString setString str TEXT LONGVARCHAR getString setString str TEXT NCLOB getString setString str getNClob TEXT SQLXML getString setString str getSQLXML -------- ------------------------ ------------------- -------------- ----------------- --------------- STRING NVARCHAR getString setString str STRING CHAR getString setString str STRING NCHAR getString setString str STRING VARCHAR getString setString str -------- ------------------------ ------------------- -------------- ----------------- --------------- ARRAY getObject getArray OBJECT getObject getObject NULL getObject getObject REF getObject getRef ROWID getObject getRowId RESULTSET getObject getObject TIME_WITH_TIMEZONE getObject getTime TIMESTAMP_WITH_TIMEZONE getObject getTimeStamp -------- ------------------------ ------------------- -------------- ----------------- --------------- * ASCII_STREAM getAsciiStream * BINARY_STREAM getBinaryStream * CHARACTER_STREAM getCharacterStream * ASCII_STREAM getAsciiStream * BINARY_STREAM getBinaryStream * CHARACTER_STREAM getCharacterStream * NCHARACTER_STREAM getNCharacterStream * URL getURL ======== ======================== =================== ============== ================= =============== Some of these types never correspond to a SQL type but are used only to specify getters and setters for a particular parameter or column. Other ----- The default getter will attempt to look for the column type by name if the type is OTHER. This allows for user defined types to be added if supported by the database. User defined types ------------------ A user can declare a new type using ``JDBCType``. The arguments are the name of new type which must match a SQL typename. Use ``typeinfo`` on the connection to get the list of available types. It may necessary to define a custom getter function which defining a new type so that the custom return type accurately reflects the column type. .. code-block:: python class JSONType(dbapi2.JDBCType): def get(self, *args): rc = JDBCType.get(self, *args) # Custom return converter here return rc JSON = JSONType("JSON") Interactions with prepared statements ------------------------------------- Certain calls can be problematic for dbapi2 depending on driver. In particular, SQL calls which invalidate the state of the connection will issue an exception when the connection is used. In HSQLDB the statement ``cur.execute('shutdown')`` invalidates the connection which when the statement is then automatically closed will then produce an exception. This exception is due to a conflict between dbapi2, Java, and HSQLDB specifications. Dbapi2 requires that statements be executed as prepared statements, Java requires that closing a statement yields no action if the connection is already closed, and HSQLBD sets the ``isValid`` to false but not ``isClosed``. Thus executing a shutdown through dbapi2 would be expected to close the prepared statement on an invalid connection resulting in an error. We can address these sort of driver specific behaviors by applying a customizer to the Java class to add additional behaviors. .. code-block:: python @jpype.JImplementationFor("java.sql.PreparedStatement") class MyStatement(object): @jpype.JOverride(sticky=True, rename='_close') def close(self): if not self.getConnection().isValid(100): return return self._close() Alternatively we can access the ``java.sql.Connection`` directly and call the shutdown routine using an unprepared statement. Though that would require accessing private fields. Conclusion ========== This wraps up the JPype dbapi2 module. Because JDBC supports many different dataase drivers, not every behavior is defined on every driver. Consult the driver specific information to determine what is available. The dbapi2 does not fully cover all of the capabilities of the JDBC driver. To access functions that are not defined in DBAPI2, the JDBC native objects can be accessed on both the connection and the cursor objects. .. _PEP-0249: https://www.python.org/dev/peps/pep-0249/ .. _connection.rollback: #jpype.dbapi2.Connection.rollback .. _connection.commit: #jpype.dbapi2.Connection.commit .. _connection.adapters: #jpype.dbapi2.JDBCType.adapters .. _connection.setters: #jpype.dbapi2.JDBCType.setters .. _connection.converters: #jpype.dbapi2.JDBCType.converters .. _cursor.use: #jpype.dbapi2.Cursor.use .. _cursor.description: #jpype.dbapi2.Cursor.description .. _jdbctype.adapters: #jpype.dbapi2.Connection.adapters jpype-1.5.0/doc/develguide.rst000066400000000000000000001401771453713375400163060ustar00rootroot00000000000000Developer Guide =============== Overview -------- This document describes the guts of jpype. It is intended lay out the architecture of the jpype code to aid intrepid lurkers to develop and debug the jpype code once I am run over by a bus. For most of this document I will use the royal we, except where I am giving personal opinions expressed only by yours truly, the author Thrameos. History ~~~~~~~ When I started work on this project it had already existed for over 10 years. The original developer had intended a much larger design with modules to support multiple languages such as Ruby. As such it was constructed with three layers of abstraction. It has a wrapper layer over Java in C++, a wrapper layer for the Python api in C++, and an abstraction layer intended to bridge Python and other interpreted languages. This multilayer abstraction ment that every debugging call had to drop through all of those layers. Memory management was split into multiple pieces with Java controlling a portion of it, C++ holding a bunch of resources, Python holding additional resources, and HostRef controlling the lifetime of objects shared between the layers. It also had its own reference counting system for handing Java references on a local scale. This level of complexity was just about enough to scare off all but the most hardened programmer. Thus I set out to eliminate as much of this as I could. Java already has its own local referencing system to deal in the form of LocalFrames. It was simply a matter of setting up a C++ object to hold the scope of the frames to eliminate that layer. The Java abstraction was laid out in a fashion somewhat orthagonally to the Java inheritance diagram. Thus that was reworked to something more in line which could be safely completed without disturbing other layers. The multilanguage abstraction layer was already pierced in multiple ways for speed. However, as the abastraction interwove throughout all the library it was a terrible lift to remove and thus required gutting the Python layer as well to support the operations that were being performed by the HostRef. The remaining codebase is fairly slim and reasonably streamlined. This rework cut out about 30% of the existing code and sped up the internal operations. The Java C++ interface matches the Java class hierachy. Architecture ~~~~~~~~~~~~ JPype is split into several distinct pieces. ``jpype`` Python module The majority of the front end logic for the toolkit is in Python jpype module. This module deals with the construction of class wrappers and control functions. The classes in the layer are all prefixed by ``J``. ``_jpype`` CPython module The native module is supported by a CPython module called ``_jpype``. The ``_jpype`` module is located in ``native/python`` and has C style classes with a prefix ``PyJP``. This CPython layer acts as a front end for passing to the C++ layer. It performs some error checking. In addition to the module functions in ``_JModule``, the module has multiple Python classes to support the native jpype code such as ``_JClass``, ``_JArray``, ``_JValue``, ``_JValue``, etc. CPython API wrapper In addition to the exposed Python module layer, there is also a C++ wrapper for the Python API. This is located in ``native/python`` and has the prefix ``JPPy`` for all classes. ``jp_pythontypes`` wraps the required parts of the CPython API in C++ for use in the C++ layer. C++ JNI layer The guts that drive Java are in the C++ layer located in ``native/common``. This layer has the namespace ``JP``. The code is divided into wrappers for each Java type, a typemanager for mapping from Java names to class instances, support classes for proxies, and a thin JNI layer used to help ensure rigerous use of the same design patterns in the code. The primary responsibility of this layer is type conversion and matching of method overloads. Java layer In addition to the C++ layer, jpype has a native Java layer. This code is compiled as a "thunk" which is loaded into the JVM in the form of a a binary stored as a string. Code for Java is found in ``native/java``. The Java layer is divided into two parts, a bootstrap loader and a jar containing the support classes. The Java layer is responsible managing the lifetime of shared Python, Java, and C++ objects. ``jpype`` module ----------------- The ``jpype`` module itself is made of a series of support classes which act as factories for the individual wrappers that are created to mirror each Java class. Because it is not possible to wrap all Java classes with staticly created wrappers, instead jpype dynamically creates Python wrappers as requested by the user. The wrapping process is triggered in two ways. The user can manually request creating a class by importing a class wrapper with jpype.imports or ``JPackage`` or by manually invoking it with ``JClass``. Or the class wrapper can be created automatically as a result of a return type or exception thrown to the user. Because the classes are created dynamically, the class structure uses a lot of Python meta programming. Each class wrapper derives from the class wrappers of each of the wrappers corresponding to the Java classes that each class extends and implements. The key to this is to hacked ``mro``. The ``mro`` orders each of the classes in the tree such that the most drived class methods are exposed, followed by each parent class. This must be ordered to break ties resulting from multiple inheritance of interfaces. The factory classes are grafted into the type system using ``__instancecheck__`` and ``__subtypecheck__``. resource types ~~~~~~~~~~~~~~ JPype largely maps to the same concepts as Python with a few special elements. The key concept is that of a Factory which serves to create Java resources dynamically as requested. For example there is no Python notation to create a ``int[][]`` as the concept of dimensions are fluid in Python. Thus a factory type creates the actual object instance type with ``JArray(JInt,2)`` Like Python objects, Java objects derives from a type object which is called ``JClass`` that serves as a meta type for all Java derived resources. Additional type like object ``JArray`` and ``JInterface`` serve to probe the relationships between types. Java object instances are created by calling the Java class wrapper just like a normal Python class. A number of pseudo classes serve as placeholders for Java types so that it is not necessary to create the type instance when using. These aliased classes are ``JObject``, ``JString``, and ``JException``. Underlying all Java instances is the concept of a ``jvalue``. ``jvalue`` ++++++++++ In the earlier design, wrappers, primitives and objects were all seperate concepts. At the JNI layer these are unified by a common element called jvalue. A ``jvalue`` is a union of all primitives with the jobject. The jobject can represent anything derived from Java object including the pseudo class jstring. This has been replaced with a Java slot concept which holds an instance of ``JPValue`` which holds a pointer to the C++ Java type wrapper and a Java jvalue union. We will discuss this object further in the CPython section. .. _bootstrapping: Bootstrapping ~~~~~~~~~~~~~ The most challenging part in working with the jpype module other than the need to support both major Python versions with the same codebase is the bootstrapping of resources. In order to get the system working, we must pass the Python resources so the ``_jpype`` CPython module can acquire resources and then construct the wrappers for ``java.lang.Object`` and ``java.lang.Class``. The key difficulty is that we need reflection to get methods from Java and those are part of ``java.lang.Class``, but class inherits from ``java.lang.Object``. Thus Object and the interfaces that Class inherits must all be created blindly. The order of bootstrapping is controlled by specific sequence of boot actions after the JVM is started in ``startJVM``. The class instance ``class_`` may not be accessed until after all of the basic class, object, and exception types have been loaded. Factories ~~~~~~~~~ The key objects exposed to the user (``JClass``, ``JObject``, and ``JArray``) are each factory meta classes. These classes serve as the gate keepers to creating the meta classes or object instances. These factories inherit from the Java class meta and have a ``class_`` instance inserted after the the JVM is started. They do not have exposed methods as they are shadows for action for actual Java types. The user calls with the specified arguments to create a resource. The factory calls the ``__new__`` method when creating an instance of the derived object. And the C++ wrapper calls the method with internally construct resource such as ``_JClass`` or ``_JValue``. Most of the internal calls currently create the resource directly without calling the factories. The gateway for this is ``PyJPValue_create`` which delegates the process to the corresponding specialized type. Style ~~~~~ One of the aspects of the jpype design is elegance of the factory patterns. Rather than expose the user a large number of distinct concepts with different names, the factories provide powerfull functionality with the same syntax for related things. Boxing a primitive, casting to a specific type, and creating a new object are all tied together in one factory, ``JObject``. By also making that factory an effective base class, we allow it to be used for ``issubtype`` and ``isinstance``. This philosophy is further enhanced by silent customizers which integrate Python functionality into the wrappers such that Java classes can be used effectively with Python syntax. Consistent use and misuse of Python concepts such as ``with`` for defining blocks such as try with resources and synchronized hide the underlying complexity and give the feeling to the user that the module is integrated completely as a solution such as jython. When adding a new feature to the Python layer, consider carefully if the feature needs to be exposed a new function or if it can be hidden in the normal Python syntax. JPype does somewhat break the Python naming conventions. Because Java and Python have very different naming schemes, at least part of the kit would have a different convention. To avoid having one portion break Python conventions and another part conform, we choose to use Java notation consistently throughout. Package names should be lower with underscores, classes should camel case starting upper, functions and method should be camel case starting lower. All private methods and classes start with a leading underscore and are not exported. Customizers ~~~~~~~~~~~ There was a major change in the way the customizers work between versions. The previous system was undocumented and has now been removed, but as someone may have used of it previously, we will contrast it with the revised system so that the customizers can be converted. In the previous system, a global list stored all customizers. When a class was created, it went though the list and asked the class if it matched that class name. If it matched, it altered the dict of members to be created so when the dynamic class was finished it had the custome behavior. This system wasn't very scalable as each customizer added more work to the class construction process. The revised system works by storing a dictionary keyed to the class name. Thus the customizer only applies to the specific class targeted to the customizer. The customizer is specified using annotation of a prototype class making methods automatically copy onto the class. However, sometimes a customizer needs to be applied to an entire tree of classes such as all classes that implement ``java.util.List``. To handle this case, the class creation system looks for a special method ``__java_init__`` in the tree of base classes and calls it on the newly created class. Most of the time the customization was the same simple pattern so we added a ``sticky`` flag to build the initialization method directly. This method can alter the class to make it add the new behavior. Note the word alter. Where before we changed the member prior to creating the class, here we are altering the class. Thus the customizer is expected to monkey patch the existing class. There is only one pattern of monkey patching that works on both Python 2 and Python 3 so be sure to use the ``type.__setattr__`` method of altering the class dictionary. It is possible to apply customizers after the class has already been created because we operate by monkey patching. But there is a limitation that there can only be one ``__java_init__`` method and thus two customizers specifying a global behavior on the same class wrapper will lead to unexpected behavior. ``_jpype`` CPython module -------------------------- Diving deeper into the onion, we have the Python front end. This is divided into a number of distinct pieces. Each piece is found under ``native/python`` and is named according to the piece it provides. For example, ``PyJPModule`` is found in the file ``native/python/pyjp_module.cpp`` Earlier versions of the module had all of the functionality in the modules global space. This functionality is now split into a number of classes. These classes each have a constructor that is used to create an instance which will correspond to a Java resource such as class, array, method, or value. Jpype objects work with the inner layers by inheriting from a set of special ``_jpype`` classes. This class hiarachy is mantained by the meta class ``_jpype._JClass``. The meta class does type hacking of the Python API to insert a reserved memory slot for the ``JPValue`` structure. The meta class is used to define the Java base classes: * ``_JClass`` - Meta class for all Java types which maps to a java.lang.Class extending Python type. * ``_JArray`` - Base class for all Java array instances. * ``_JObject`` - Base type of all Java object instances extending Python object. * ``_JNumberLong`` - Base type for integer style types extending Python int. * ``_JNumberFloat`` - Base type for float style types extending Python float. * ``_JNumberChar`` - Special wrapper type for JChar and java.lang.Character types extending Python float. * ``_JException`` - Base type for exceptions extending Python Exception. * ``_JValue`` - Generic capsule representing any Java type or instance. These types are exposed to Python to implement Python functionality specific to the behavior expected by the Python type. Under the hood these types are largely ignored. Instead the internal calls for the Java slot to determine how to handle the type. Therefore, internally often Python methods will be applied to the "wrong" type as the requirement for the method can be satisfied by any object with a Java slot rather than a specific type. See the section regarding Java slots for details. ``PyJPModule`` module ~~~~~~~~~~~~~~~~~~~~~~ This is the front end for all the global functions required to support the Python native portion. Most of the functions provided in the module are for control and auditing. Resources are created by setting attributes on the ``_jpype`` module prior to calling ``startJVM``. When the JVM is started each of th required resources are copied from the module attribute lists to the module internals. Setting the attributes after the JVM is started has no effect. Resources are verified to exist when the JVM is started and any missing resource are reported as an error. ``_JClass`` class ~~~~~~~~~~~~~~~~~~~ The class wrappers have a metaclass ``_jpyep._JClass`` which serves as the guardian to ensure the slot is attached, provide for the inheritance checks, and control access to static fields and methods. The slot holds a java.lang.Class instance but it does not have any of the methods normally associate with a Java class instance exposed. A java.lang.Class instance can be converted to a Jave class wrapper using ``JClass``. ``_JMethod`` class ~~~~~~~~~~~~~~~~~~~~ This class acts as descriptor with a call method. As a descriptor accessing its methods through the class will trigger its ``__get__`` function, thus getting ahold of it within Python is a bit tricky. The ``__get__`` mathod is used to bind the static unbound method to a particular object instance so that we can call with the first argument as the ``this`` pointer. It has some reflection and diagnostics methods that can be useful it tracing down errors. The beans methods are there just to support the old properties API. The naming on this class is a bit deceptive. It does not correspond to a single method but rather all the overloads with the same name. When called it passes to with the arguments to the C++ layer where it must be resolved to a specific overload. This class is stored directly in the class wrappers. ``_JField`` class ~~~~~~~~~~~~~~~~~~~ This class is a descriptor with ``__get__`` and ``__set__`` methods. When called at the static class layer it operates on static fields. When called on a Python object, it binds to the object making a ``this`` pointer. If the field is static, it will continue to access the static field, otherwise, it will provide access to the member field. This trickery allows both static and member fields to wrap as one type. This class is stored directly in the class wrappers. ``_JArray`` class ~~~~~~~~~~~~~~~~~~~ Java arrays are extensions of the Java object type. It has both methods associated with java.lang.Object and Python array functionality. Primitives have specialized implementations to allow for the Python buffer API. ``_JMonitor`` class ~~~~~~~~~~~~~~~~~~~~~ This class provides ``synchronized`` to JPype. Instances of this class are created and held using ``with``. It has two methods ``__enter__`` and ``__exit__`` which hook into the Python RAII system. ``_JValue`` class ~~~~~~~~~~~~~~~~~~~ Java primitive and object instance derive from special Python derived types. These each have the Python functionality to be exposed and a Java slot. The most generic of these is ``_JValue`` which is simply a capsule holding the Java C++ type wrapper and a Java jvalue union. CPython methods for the ``PyJPValue`` apply to all CPython objects that hold a Java slot. Specific implementation exist for object, numbers, characters, and exceptions. But fundimentally all are treated the same internally and thus the CPython type is effectively erased outside of Python. Unlike ``jvalue`` we hold the object type in the C++ ``JPValue`` object. The class reference is used to determine how to match the arguments to methods. The class may not correspond to the actual class of the object. Using a class other than the actual class serves to allow an object to be cast and thus treated like another type for the purposes of overloading. This mechanism is what allows the ``JObject`` factory to perform a typecast to make an object instance act like one of its base classes.. .. _javaslots: Java Slots ------------------ THe key to achieving reasonable speed within CPython is the use of slots. A slot is a dedicated memory location that can be accessed without consulting the dictionary or bases of an object. CPython achieve this by reserving space within the type structure and by using a set of bit flags so that it can avoid costly. The reserved space in order by number and thus avoids the need to access the dictionary while the bit flags serve to determine the type without traversing the ``__mro__`` structure. We had to implement the same effect which deriving from a wide variety for Python types including type, object, int, long, and Exception. Adding the slot directly to the type and objects base memory does not work because these types all have different memory layouts. We could have a table look up based on the type but because we must obey both the CPython and the Java object hierarchy at the same time it cannot be done within the memory layout of Python objects. Instead we have to think outside the box, or rather outside the memory footprint of Python objects. CPython faces the same conflict internally as inheritance often forces adding a dictionary or weak reference list onto a variably size type sych as long. For those cases it adds extract space to the basesize of the object and then ignores that space for the purposes of checking inheritance. It pairs this with an offset slot that allows for location of the dynamic placed slots. We cannot replicate this in the same way because the CPython interals are all specialize static members and there is no provision for introducting user defined dynamic slots. Therefore, instead we will add extra memory outside the view of Python objects though the use of a custom allocator. We intercept the call to create an object allocation and then call the regular Python allocators with the extra memory added to the request. As our extrs slot has resource in the form of Java global references associated with it, we must deallocate those resource regardless of the type that has been extended. We perform this task by creating a custom finalize method to serve as the destructor. Thus a Java slot requires overriding each of ``tp_alloc``, ``tp_free`` and ``tp_finalize``. The class meta gatekeeper creates each type and verifies that the required hooks are all in place. If the user tries to bypass this it should produce an error. In place of Python bit flags to check for the presence of a Java slot we instead test the slot table to see if our hooks are in place. We can test if the slot is present by looking to see if both `tp_alloc` and `tp_finalize` point to our Java slot handlers. This means we are still effectively a slot as we can test and access with O(1). Accessing the slot requires testing if the slot exists for the object, then computing the sice of the object using the basesize and itemsize associate with the type and then offsetting the Python object pointer appropriately. The overall cost is O(1), though is slightly more heavy that directly accesssing an offset. CPython API layer ------------------ To make creation of the C++ layer easier a thin wrapper over the CPython API was developed. This layer provided for handling the CPython referencing using a smart pointer, defines the exception handling for Python, and provides resource hooks for duck typing of the ``_jpype`` classes. This layer is located with the rest of the Python codes in ``native/python``, but has the prefix ``JPPy`` for its classes. As the bridge between Python and C++, these support classes appear in both the ``_jpype`` CPython module and the C++ JNI layer. Exception handling ~~~~~~~~~~~~~~~~~~ A key piece of the jpype interaction is the transfer of exceptions from Java to Python. To accomplish this Python method that can result in a call to Java must have a ``try`` block around the contents of the function. We use a routine pattern of code to interact with Java to achieve this: .. code-block:: cpp PyObject* dosomething(PyObject* self, PyObject* args) { // Tell the logger where we are JP_PY_TRY("dosomething"); // Make sure there is a jvm to receive the call. ASSERT_JVM_RUNNING("dosomething"); // Make a resource to capture any Java local references JPJavaFrame frame; // Call our Java methods ... // Return control to Python return obj.keep(); // Use the standard catch to transfer any exceptions back // to Python JP_PY_CATCH(NULL); } All entry points from Python into ``_jpype`` should be guarded with this pattern. There are exceptions to this pattern such as removing the logging, operating on a call that does not need the JVM running, or operating where the frame is already supported by the method being called. Python referencing ~~~~~~~~~~~~~~~~~~ One of the most miserable aspects of programming with CPython is the relative inconsistancy of referencing. Each method in Python may use a Python object or steal it, or it may return a borrowed reference or give a fresh reference. Similar command such as getting an element from a list and getting an element from a tuple can have different rules. This was a constant source of bugs requiring consultation of the Python manual for every line of code. Thus we wrapped all of the Python calls we were required to work with in ``jp_pythontypes``. Included in this wrapper is a Python reference counter called ``JPPyObject``. Whenever an object is returned from Python it is immediately placed in smart pointer ``JPPyObject`` with the policy that it was created with such as ``use_``, ``borrowed_``, ``claim_`` or ``call_``. ``use_`` This policy means that the reference counter needs to be incremented and the start and the end. We must reference it because if we don't and some Python call destroys the refernce out from under us, the system may crash and burn. ``borrowed_`` This policy means we were to be give a borrowed reference that we are expected to reference and unreference when complete, but the command that returned it can fail. Thus before reference it, the system must check if an error has occurred. If there is an error, it is promoted to an exception. ``claim_`` This policy is used when we are given a new object with is already referenced for us. Thus we are to steal the reference for the duration of our use and then dereference when we are done to keep it from leaking. ``call_`` This policy both steals the reference and verifies there were no errors prior to continuing. Errors are promoted to exceptions when this reference is created. If we need to pass an object which is held in a smart pointer to Python which requires a reference, we call keep on the reference which transfers control to a ``PyObject*`` and prevents the pointer from removing the reference. As the object handle is leaving our control keep should only be called the return statement. The smart pointer is not used on method passing in which the parent explicitly holds a reference to the Python object. As all tuples passed as arguments operate like this, that means much of the API accepts bare ``PyObject*`` as arguments. It is the job of the caller to hold the reference for its scope. On CPython extensions ~~~~~~~~~~~~~~~~~~~~~ CPython is somewhat of a nightmare to program in. It is not that they did not try to document the API, but it is darn complex. The problems extend well beyond the reference counting system that we have worked around. In particular, the object model though well developed is very complex, often to get it to work you must follow letter for letter the example on the CPython user guide, and even then it may all go into the ditch. The key problem is that there are a lot of very bad examples of how to write CPython extension modules out there. Often the these examples bypass the appropriate macro and just call the field, or skip the virtual table and try to call the Python method directly. It is true that these things do not break there example, but they are conditioned on these methods they are calling directly to be the right one for the job, but depends a lot on what the behavior of the object is supposed to be. Get it wrong and you get really nasty segfault. CPython itself may be partly responsible for some of these problems. They generally seem to trust the user and thus don't verify if the call makes sense. It is true that it will cost a little speed to be aggressive about checking the type flags and the allocator match, but not checking when the error happens, means that it fails far from the original problem source. I would hope that we have moved beyond the philosophy that the user should just to whatever they want so it runs as fast as possible, but that never appears to be the case. Of course, I am just opining from the outside of the tent and I am sure the issues are much more complicated it appears superficially. Then again if I can manage to provide a safe workspace while juggling the issues of multiple virtual machines, I am free to have opinions on the value of trading performance and safety. In short when working on the extension code, make sure you do everything by the book, and check that book twice. Always go through the types virtual table and use the propery macros to access the resources. Miss one line in some complex pattern even once and you are in for a world of hurt. There are very few guard rails in the CPython code. C++ JNI layer ------------- The C++ layer has a number of tasks. It is used to load thunks, call JNI methods, provide reflection of classes, determine if a conversion is possible, perform conversion, match arguments to overloads, and convert return values back to Java. Memory management ~~~~~~~~~~~~~~~~~ Java provides built in memory management for controlling the lifespan of Java objects that are passed through JNI. When a Java object is created or returned from the JVM it returns a handle to object with a reference counter. To manage the lifespan of this reference counter a local frame is created. For the duration of this frame all local references will continue to exist. To extend the lifespan either a new global reference to the object needs to be created, or the object needs to be kept. When the local frame is destroyed all local references are destroyed with the exception of an optional specified local return reference. We have wrapped the Java reference system with the wrapper ``JPLocalFrame``. This wrapper has three functions. It acts as a RAII (Resource acquisition is initialization) for the local frame. Further, as creating a local frame requires creating a Java env reference and all JNI calls require access to the env, the local frame acts as the front end to call all JNI calls. Finally as getting ahold of the env requires that the thread be attached to Java, it also serves to automatically attach threads to the JVM. As accessing an unbound thread will cause a segmentation fault in JNI, we are now safe from any threads created from within Python even those created outside our knowledge. (I am looking at you spyder) Using this pattern makes the JPype core safe by design. Forcing JNI calles to be called using the frame ensures: - Every local reference is destroyed. - Every thread is properly attached before JNI is used. - The pattern of keep only one local reference is obeyed. To use a local frame, use the pattern shown in this example. .. code-block:: cpp jobject doSomeThing(std::string args) { // Create a frame at the top of the scope JPLocalFrame frame; // Do the required work jobject obj =frame.CallObjectMethodA(globalObj, methodRef, params); // Tell the frame to return the reference to the outer scope. // once keep is called the frame is destroyed and any // call will fail. return frame.keep(obj); } Note that the value of the object returned and the object in the function will not be the same. The returned reference is owned by the enclosing local frame and points to the same object. But as its lifespan belongs to the outer frame, its location in memory is different. You are allowed to ``keep`` a reference that was global or was passed in, in either of those case, the outer scope will get a new local reference that points to the same object. Thus you don't need to track the origin of the object. The changing of the value while pointing is another common problem. A routine error is to get a local reference, call ``NewGlobalRef`` and then keeping the local reference rather than the shiny new global reference it made. This is not like the Python reference system where you have the object that you can ref and unref. Thus make sure you always store only the global reference. .. code-block:: cpp jobject global; // we are getting a reference, may be local, may be global. // either way it is borrowed and it doesn't belong to us. void elseWhere(jvalue value) { JPLocalFrame frame; // Bunch of code leading us to decide we need to // hold the resource longer. if (cond) { // okay we need to keep this reference, so make a // new global reference to it. global = frame.NewGlobalRef(value.l); } } But don't mistake this as an invitation to make global references everywhere. Global reference are global, thus will hold the member until the reference is destroyed. C++ exceptions can lead to missing the unreference, thus global references should only happen when you are placing the Java object into a class member variable or a global variable. To help manage global references, we have ``JPRef<>`` which holds a global reference for the duration of the C++ lifespace. This is the base class for each of the global reference types we use. .. code-block:: cpp typedef JPRef JPClassRef; typedef JPRef JPObjectRef; typedef JPRef JPArrayRef; typedef JPRef JPThrowableRef; For functions that expect the outer scope to already have created a frame for this context, we use the pattern of extending the outer scope rather than creating a new one. .. code-block:: cpp jobject doSomeThing(JPLocalFrame& frame, std::string args) { // Do the required work jobject obj = frame.CallObjectMethodA(globalObj, methodRef, params); // We must not call keep here or we will terminate // a frame we do not own. return obj; } Although the system we have set up is "safe by design", there are things that can go wrong is misused. If the caller fails to create a frame prior to calling a function that returns a local reference, the reference will go into the program scoped local references and thus leak. Thus, it is usually best to force the user to make a scope with the frame extension pattern. Second, if any JNI references that are not kept or converted to global, it becomes invalid. Further, since JNI recycles the reference pointer fairly quickly, it most likely will be pointed to another object whose type may not be expected. Thus, best case is using the stale reference will crash and burn. Worse case, the reference will be a live reference to another object and it will produce an error which seems completely irrelevant to anything that was being called. Horrible case, the live object does not object to bad call and it all silently proceeds down the road another two miles before coming to flaming death. Moral of the story, always create a local frame even if you are handling a global reference. If passed or returned a reference of any kind, it is a borrowed reference belonging to the caller or being held by the current local frame. Thus it must be treated accordingly. If you have to hold a global use the appropraite ``JPRef`` class to ensure it is exception and dtor safe. For further information read ``native/common/jp_javaframe.h``. Type wrappers ~~~~~~~~~~~~~ Each Java type has a C++ wrapper class. These classes provide a number of methods. Primitives each have their own unit type wrapper. Object, arrays, and class instances share a C++ wrapper type. Special instances are used for ``java.lang.Object`` and ``java.lang.Class``. The type wrapper are named for the class they wrap such as ``JPIntType``. Type conversion ++++++++++++++++ For type conversion, a C++ class wrapper provides four methods. ``canConvertToJava`` This method must consult the supplied Python object to determine the type and then make a determination of whether a conversion is possible. It reports ``none_`` if there is no possible conversion, ``explicit_`` if the conversion is only acceptable if forced such as returning from a proxy, ``implicit_`` if the conversion is possible and acceptable as part of an method call, or ``exact_`` if this type converts without ambiguity. It is excepted to check for something that is already a Java resource of the correct type such as ``JPValue``, or something this is implementing the behavior as an interface in the form of a ``JPProxy``. ``convertToJava`` This method consults the type and produces a conversion. The order of the match should be identical to the ``canConvertToJava``. It should also handle values and proxies. ``convertToPythonObject`` This method takes a jvalue union and converts it to the corresponding Python wrapper instance. ``getValueFromObject`` This converts a Java object into a ``JPValue`` corresponding. This unboxes primitives. Array conversion ++++++++++++++++++ In addition to converting single objects, the type rewrappers also serve as the gateway to working with arrays of the specified type. Five methods are used to work with arrays: ``newArrayInstance``, ``getArrayRange``, ``setArrayRange``, ``getArrayItem``, and ``setArrayItem``. Invocation and Fields ++++++++++++++++++++++ To convert a return type produced from a Java call, each type needs to be able to invoke a method with that return type. This corresponses the underlying JNI design. The methods invoke and invokeStatic are used for this purpose. Similarly accessing fields requires type conversion using the methods ``getField`` and ``setField``. Instance versus Type wrappers +++++++++++++++++++++++++++++++ Instances of individual Java classes are made from ``JPClass``. However, two special sets of conversion rules are required. These are in the form of specializations ``JPObjectBaseClass`` and ``JPClassBaseClass`` corresponding to ``java.lang.Object`` and ``java.lang.Class``. Support classes ~~~~~~~~~~~~~~~ In addition to the type wrappers, there are several support classes. These are: ``JPTypeManager`` The typemanager serves as a dict for all type wrappers created during the operation. ``JPReferenceQueue`` Lifetime manager for Java and Python objects. ``JPProxy`` Proxies implement a Java interface in Python. ``JPClassLoader`` Loader for Java thunks. ``JPEncoding`` Decodes and encodes Java UTF strings. ``JPTypeManager`` ++++++++++++++++++ C++ typewrappers are created as needed. Instance of each of the primitives along with ``java.lang.Object`` and ``java.lang.Class`` are preloaded. Additional instances are created as requested for individual Java classes. Currently this is backed by a C++ map of string to class wrappers. The typemanager provides a number lookup methods. .. code-block:: cpp // Call from within Python JPClass* JPTypeManager::findClass(const string& name) // Call from a defined Java class JPClass* JPTypeManager::findClass(jclass cls) // Call used when returning an object from Java JPClass* JPTypeManager::findClassForObject(jobject obj) ``JPReferenceQueue`` ++++++++++++++++++++ When a Python object is presented to Java as opposed to a Java object, the lifespan of the Python object must be extended to match the Java wrapper. The reference queue adds a reference to the Python object that will be removed by the Java layer when the garbage collection deletes the wrapper. This code is almost entirely in the Java library, thus only the portion to support Java native methods appears in the C++ layer. Once started the reference queue is mostly transparent. registerRef is used to bind a Python object live span to a Java object. .. code-block:: cpp void JPReferenceQueue::registerRef(jobject obj, PyObject* hostRef) ``JPProxy`` ++++++++++++ In order to call Python functions from within Java, a Java proxy is used. The majority of the code is in Java. The C++ code holds the Java native portion. The native implement of the proxy call is the only place in with the pattern for reflecting Python exceptions back into Java appears. As all proxies are ties to Python references, this code is strongly tied to the reference queue. ``JPClassLoader`` ++++++++++++++++++ This code is responsible for loading the Java class thunks. As it is difficult to ensure we can access a Java jar from within Python, all Java native code is stored in a binary thunk compiled into the C++ layer as a header. The class loader provides a way to load this embedded jar first by bootstrapping a custom Java classloader and then using that classloader to load the internal jar. The classloader is mostly transparent. It provides one method called findClass which loads a class from the internal jar. .. code-block:: cpp jclass JPClassLoader::findClass(string name) ``JPEncoding`` +++++++++++++++ Java concept of UTF is pretty much out of sync with the rest of the world. Java used 16 bits for its native characters. But this was inadequate for all of the unicode characters, thus longer unicode character had to be encoded in the 16 bit space. Rather the directly providing methods to convert to a standard encoding such as UTF8, Java used UTF16 encoded in 8 bits which they dub Modified-UTF8. ``JPEncoding`` deals with converting this unusual encoding into something that Python can understand. The key method in this module is transcribe with signature .. code-block:: cpp std::string transcribe(const char* in, size_t len, const JPEncoding& sourceEncoding, const JPEncoding& targetEncoding) There are two encodings provided, ``JPEncodingUTF8`` and ``JPEncodingJavaUTF8``. By selecting the source and traget encoding transcribe can convert to or from Java to Python encoding. Incidentally that same modified UTF coding is used in storing symbols in the class files. It seems like a really poor design choice given they have to document this modified UTF in multiple places. As far as I can tell the internal converter only appears on ``java.io.DataInput`` and ``java.io.DataOutput``. Java native code ---------------- At the lowest level of the onion is the native Java layer. Although this layer is most remote from Python, ironically it is the easiest layer to communicate with. As the point of jpype is to communicate with Java, it is possible to directly communicate with the jpype Java internals. These can be imported from the package ``org.jpype``. The code for the Java layer is located in ``native/java``. It is compiled into a jar in the build directory and then converted to a C++ header to be compiled into the ``_jpype`` module. The Java layer currently houses the reference queue, a classloader which can load a Java class from a bytestream source, the proxy code for implementing Java interfaces, and a memory compiler module which allows Python to directly create a class from a string. Tracing --------- Because the relations between the layers can be daunting especially when things go wrong. The CPython and C++ layer have a built in logger. This logger must be enabled with a compiler switch to activate. To active the logger, touch one of the cpp files in the native directory to mark the build as dirty, then compile the ``jpype`` module with: :: python setup.py --enable-tracing develop Once built run a short test program that demonstrates the problem and capture the output of the terminal to a file. This should allow the developer to isolate the fault to specific location where it failed. To use the logger in a function start the ``JP_TRACE_IN(function_name)`` which will open a ``try catch`` block. The JPype tracer can be augmented with the Python tracing module to give a very good picture of both JPype and Python states at the time of the crash. To use the Python tracing, start Python with... :: python -m trace --trace myscript.py Debugging issues ---------------- If the tracing function proves inadequate to identify a problem, we often need to turn to a general purpose tool like gdb or valgrind. The JPype core is not easy to debug. Python can be difficult to properly monitor especially with tools like valgrind due to its memory handling. Java is also challenging to debug. Put them together and you have the mother of all debugging issues. There are a number of complicating factors. Let us start with how to debug with gdb. Gdb runs into two major issues, both tied to the signal handler. First, Java installs its own signal handlers that take over the entire process when a segfault occurs. This tends to cause very poor segfault stacktraces when examining a core file, which often is corrupt after the first user frame. Second, Java installs its signal handlers in such as way that attempting to run under a debugger like gdb will often immediately crash preventing one from catching the segfault before Java catches it. This makes for a catch 22, you can't capture a meaningful non-interactively produced core file, and you can't get an interactive session to work. Fortunately there are solutions to the interactive session issue. By disabling the SIGSEGV handler, we can get past the initial failure and also we can catch the stack before it is altered by the JVM. :: gdb -ex 'handle SIGSEGV nostop noprint pass' python Thus far I have not found any good solutions to prevent the JVM from altering the stack frames when dumping the core. Thus interactive debugging appears to be the best option. There are additional issues that one should be aware of. Open-JDK 1.8 has had a number of problems with the debugger. Starting JPype under gdb may trigger, may trigger the following error. :: gdb.error: No type named nmethod. There are supposed to be fixes for this problem, but none worked for me. Upgrading to Open-JDK 9 appears to fix the problem. Another complexity with debugging memory problems is that Python tends to hide the problem with its allocation pools. Rather than allocating memory when a new object is request, it will often recycle and existing object which was collect earlier. The result is that an object which turns out is still live becomes recycled as a new object with a new type. Thus suddenly a method which was expected to produce some result instead vectors into the new type table, which may or may not send us into segfault land depending on whether the old and new objects have similar memory layouts. This can be partially overcome by forcing Python to use a different memory allocation scheme. This can avoid the recycling which means we are more likely to catch the error, but at the same time means we will be excuting different code paths so we may not reach a similar state. If the core dump is vectoring off into code that just does not make sense it is likely caused by the memory pools. Starting Python 3, it is possible to select the memory allocation policy through an enviroment variable. See the ``PYTHONMALLOC`` setting for details. Future directions ----------------- Although the majority of the code has been reworked for JPype 0.7, there is still further work to be done. Almost all Java constructs can be exercised from within Python, but Java and Python are not static. Thus, we are working on further improvements to the jpype core focusing on making the package faster, more efficient, and easier to maintain. This section will discuss a few of these options. Java based code is much easier to debug as it is possible to swap the thunk code with an external jar. Further, Java has much easier management of resources. Thus pushing a portion of the C++ layer into the Java layer could further reduce the size of the code base. In particular, deciding the order of search for method overloads in C++ attempts to reconstruct the Java overload rules. But these same rules are already available in Java. Further, the C++ layer is designed to make many frequent small calls to Java methods. This is not the preferred method to operate in JNI. It is better to have specialized code in Java which preforms large tasks such as collecting all of the fields needed for a type wrapper and passing it back in a single call, rather than call twenty different general purpose methods. This would also vastly reduce the number of ``jmethods`` that need to be bound in the C++ layer. The world of JVMs is currently in flux. Jpype needs to be able to support other JVMs. In theory, so long a JVM provides a working JNI layer, there is no reason the jpype can't support it. But we need loading routines for these JVMs to be developed if there are differences in getting the JVM launched. There is a project page on github shows what is being developed for the next release. Series 0.6 was usable, but early versions had notable issues with threading and internal memory management concepts had to be redone for stability. Series 0.7 is the first verion after rewrite for simplication and hardening. I consider 0.7 to be at the level of production quality code suitable for most usage though still missing some needed features. Series 0.8 will deal with higher levels of Python/Java integration such as Java class extension and pickle support. Series 0.9 will be dedicated to any additional hardening and edge cases in the core code as we should have complete integration. Assuming everything is completed, we will one day become a real boy and have a 1.0 release. jpype-1.5.0/doc/imports.rst000066400000000000000000000044741453713375400156650ustar00rootroot00000000000000JImport ======= Module for dynamically loading Java Classes using the import system. This is a replacement for the jpype.JPackage("com").fuzzy.Main type syntax. It features better safety as the objects produced are checked for class existence. To use Java imports, import the domains package prior to importing a Java class. This module supports three different styles of importing java classes. 1) Import of the package path ----------------------------- **import ** Importing a series of package creates a path to all classes contained in that package. The root package is added to the global scope. Imported packages are added to the directory of the base module. .. code-block:: python import java mystr = java.lang.String('hello') mylist = java.util.LinkedList() path = java.nio.files.Paths.get() 2) Import of the package path as a module ----------------------------------------- **import as ** A package can be imported as a local variable. This provides access to all Java classes in that package including contained packages. Example: .. code-block:: python import java.nio as nio bb = nio.ByteBuffer() path = nio.file.Path() 3) Import a class from an object -------------------------------- **from import [,\*] [as ]** An individual class can be imported from a java package. This supports inner classes as well. Example: .. code-block:: python # Import one class from java.lang import String mystr = String('hello') # Import multiple classes from java.lang import Number,Integer,Double # Import java inner class java.lang.ProcessBuilder.Redirect from java.lang.ProcessBuilder import Redirect This method can also be used to import a static variable or method from a class. Wildcards import all packages and public classes into the global scope. Import caveats -------------- Keyword naming ~~~~~~~~~~~~~~ Occasionally a java class may contain a python keyword. Python keywords as automatically remapped using trailing underscore. Example:: from org.raise_ import Object => imports "org.raise.Object" Limitations ~~~~~~~~~~~ * Non-static members can be imported but can not be called without an instance. JPype does not provide an easy way to determine which functions objects can be called without an object. jpype-1.5.0/doc/index.rst000066400000000000000000000020151453713375400152640ustar00rootroot00000000000000JPype documentation =================== JPype is a Python module to provide full access to Java from within Python. It allows Python to make use of Java specific libraries, explore and visualize Java structures, develop and test Java libraries, make use of scientific computing, and much more. By enabling the use of Python for rapid prototyping and Java for strong typed production code, JPype provides a powerful environment for engineering and code development. Unlike Jython, JPype does not achieve this by re-implementing Python, but instead by interfacing both virtual machines at the native level. This shared memory based approach achieves good computing performance, while providing the access to the entirety of CPython and Java libraries. Parts of the documentation ========================== .. toctree:: :maxdepth: 2 install userguide quickguide api dbapi2 imports android CHANGELOG develguide Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` jpype-1.5.0/doc/install.rst000066400000000000000000000133531453713375400156320ustar00rootroot00000000000000Installation ============ JPype is available either as a pre-compiled binary for Anaconda, or may be built from source though various methods. Binary Install -------------- JPype can be installed as pre-compiled binary if you are using the `Anaconda `_ Python stack. Binaries are available for Linux, OSX, and windows on conda-forge. 1. Ensure you have installed Anaconda/Miniconda. Instructions can be found `here `__. 2. Install from the conda-forge software channel:: conda install -c conda-forge jpype1 Source Install -------------- Installing from source requires: Python JPype works CPython 3.5 or later. Both the runtime and the development package are required. Java Either the Sun/Oracle JDK/JRE Variant or OpenJDK. JPype source distribution includes a copy of the Java JNI header and precompiled Java code, thus the Java Development Kit (JDK) is not required. JPype has been tested with Java versions from Java 1.8 to Java 13. C++ A C++ compiler which matches the ABI used to build CPython. JDK *(Optional)* JPype contains sections of Java code. These sections are precompiled in the source distribution, but must be built when installing directly from the git repository. Once these requirements have been met, one can use pip to build from either the source distribution or directly from the repository. Specific requirements from different achitectures are listed below_. Build using pip ~~~~~~~~~~~~~~~ JPype may be built and installed with one step using pip. To install the latest JPype, use: :: pip install JPype1 This will install JPype either from source or binary distribution, depending on your operating system and pip version. To install from the current github master use: :: pip install git+https://github.com/jpype-project/jpype.git More details on installing from git can be found at `Pip install `__. The git version does not include a prebuilt jar the JDK is required. Build and install manually ~~~~~~~~~~~~~~~~~~~~~~~~~~ JPype can be built entirely from source. **1. Get the JPype source** The JPype source may be acquired from either `github `__ or from `PyPi `__. **2. Build the source with desired options** Compile JPype using the included ``setup.py`` script: :: python setup.py build The setup script recognizes several arguments. --enable-build-jar Force setup to recreate the jar from scratch. --enable-tracing Build a verison of JPype with full logging to the console. This can be used to diagnose tricky JNI issues. After building, JPype can be tested using the test bench. The test bench requires JDK to build. **3. Test JPype with (optional):** :: python setup.py test **4. Install JPype with:** :: python setup.py install If it fails... ~~~~~~~~~~~~~~ Most failures happen when setup.py is unable to find the JDK home directory which shouble be set in the enviroment variable ``JAVA_HOME``. If this happens, preform the following steps: 1. Identify the location of your systems JDK installation and explicitly passing it to setup.py. :: JAVA_HOME=/usr/lib/java/jdk1.8.0/ python setup.py install 2. If that setup.py still fails please create an Issue `on github `__ and post the relevant logs. .. _below: Platform Specific requirements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JPype is known to work on Linx, OSX, and Windows. To make it easier to those who have not built CPython modules before here are some helpful tips for different machines. Debian/Ubuntu ::::::::::::: Debian/Ubuntu users will have to install ``g++`` and ``python-dev``. Use: sudo apt-get install g++ python-dev python3-dev Windows ::::::: CPython modules must be built with the same C++ compiler used to build Python. The tools listed below work for Python 3.5 to 3.8. Check with `Python dev guide `_ for the latest instructions. 1. Install your desired version of Python (3.5 or higher), e.g., `Miniconda `_ is a good choice for users not yet familiar with the language 2. For Python 3 series, Install either 2017 or 2019 Visual Studio. `Microsoft Visual Studio 2019 Community Edition `_ is known to work. From the Python developer page: When installing Visual Studio 2019, select the Python development workload and the optional Python native development tools component to obtain all of the necessary build tools. If you do not already have git installed, you can find git for Windows on the Individual components tab of the installer. When building for windows you must use the Visual Studio developer command prompt. Path requirements ----------------- On certain systems such as Windows 2016 Server, the JDK will not load properly despite JPype properly locating the JVM library. The work around for this issue is add the JRE bin directory to the system PATH. Apparently, the shared library requires dependencies which are located in the bin directory. If a JPype fails to load despite having the correct JAVA_HOME and system architecture, it may be this issue. Known Bugs/Limitations ---------------------- - Java classes outside of a package (in the ````) cannot be imported. - Because of lack of JVM support, you cannot shutdown the JVM and then restart it. Nor can you start more than one copy of the JVM. - Mixing 64 bit Python with 32 bit Java and vice versa crashes on import of the jpype module. jpype-1.5.0/doc/java/000077500000000000000000000000001453713375400143465ustar00rootroot00000000000000jpype-1.5.0/doc/java/__init__.py000066400000000000000000000000461453713375400164570ustar00rootroot00000000000000from . import lang from . import util jpype-1.5.0/doc/java/lang.py000066400000000000000000000016511453713375400156440ustar00rootroot00000000000000import jpype class Thread: """ Thread support for Python JPype adds methods to ``java.lang.Thread`` to interact with Python threads. These methods are all classmethods that act on the current Python thread. """ isAttached = jpype._jthread._JThread.isAttached attach = jpype._jthread._JThread.attach attachAsDaemon = jpype._jthread._JThread.attachAsDaemon detach = jpype._jthread._JThread.detach class AutoCloseable: """ Customizer for ``java.lang.AutoCloseable`` and ``java.io.Closeable`` This customizer adds support of the ``with`` operator to all Java classes that implement the Java ``AutoCloseable`` interface. Example: .. code-block:: python from java.nio.file import Files, Paths with Files.newInputStream(Paths.get("foo")) as fd: # operate on the input stream # Input stream closes at the end of the block. """ ... jpype-1.5.0/doc/java/util.py000066400000000000000000000173661453713375400157120ustar00rootroot00000000000000import jpype from typing import Union class Iterable: """ Customized interface for a container which can be iterated. JPype wraps ``java.lang.Iterable`` as the Python iterator interface. """ def __iter__(self): """ Iterate over the members on this collect. """ ... class Collection: """ Customized interface representing a collection of items. JPype wraps ``java.util.Collection`` as a Python collection. """ def __len__(self) -> int: """ Get the length of this collection. Use ``len(collection)`` to find the number of items in this collection. """ ... def __delitem__(self, item): """ Collections do not support remove by index. """ ... def __contains__(self, item) -> bool: """ Check if this collection contains this item. Use ``item in collection`` to check if the item is present. Args: item: is the item to check for. This must be a Java object or an object which can be automatically converted such as a string. Returns: bool: True if the item is in the collection. """ ... class List(Collection): """ Customized container holding items in a specified order. JPype customizes ``java.lang.List`` to be equivalent to a Python list. Java list fulfill the contract for ``collection.abc.MutableSequence``. """ def __getitem__(self, ndx): """ Access an item or set of items. Use ``list[idx]`` to access a single item or ``list[i0:i1]`` to obtain a slice of the list. Slices are views thus changing the view will alter the original list. Slice stepping is not supported for Java lists. """ ... def __setitem__(self, index: Union[int, slice], value): """ Set an item on the list. Use ``list[idx]=value`` to set a value on the list or ``list[i0:i1] = values`` to replace a section of a list with another list of values. """ ... def __delitem__(self, idx: Union[int, slice]): """ Delete an item by index. Use ``del list[idx]`` to remove ont itme from the list or ``del list[i0:i1]`` to remove a section of the list. """ ... def __reversed__(self): """ Obtain an iterator that walks the list in reverse order. Use ``reversed(list)`` to traverse a list backwards. """ ... def index(self, obj) -> int: """ Find the index that an item appears. Args: obj: A Java object or Python object which automatically converts to Java. Returns: int: The index where the item first appears in the list. Raises: ValueError: If the item is not on the list. """ ... def count(self, obj): """ Count the number of times an object appears in a list. Args: obj: A Java object or Python object which automatically converts to Java. Returns: int: The number of times this object appears. """ ... def insert(self, idx: int, obj): """ Insert an object at a specific position. Args: idx: The index to insert the item in front of. obj: The object to insert. Raises: TypeError: If the object cannot be converted to Java. """ ... def append(self, obj): """ Append an object to the list. Args: obj: The object to insert. Raises: TypeError: If the object cannot be converted to Java. """ ... def reverse(self): """ Reverse the order of the list in place. This is equivalent to ``java.util.Collections.reverse(list)``. """ def extend(self, lst): """ Extends a list by adding a set of elements to the end. Args: lst: A Sequence holding items to be appended. Raises: TypeError: If the list to be added cannot be converted to Java. """ ... def pop(self, idx=-1): """ Remove an item from the list. Args: idx (int, optional): Position to remove the item from. If not specified the item is removed from the end of the list. Returns: The item or raises if index is outside of the list. """ ... def __iadd__(self, obj): """ Add an items to the end of the list. Use ``list += obj`` to append one item. This is simply an alias for add. """ ... def __add__(self, obj): """ Combine two lists. Use ``list + seq`` to create a new list with additional members. This is only supported if the list can be cloned. """ ... def remove(self, obj): """ Remove an item from the list by finding the first instance that matches. This overrides the Java method to provide the Python remove. Use ``lst.remove_`` to obtain the Java remove method. Args: obj: Must be a Java object or Python object that can convert to Java automatically. Raises: ValueError: If the item is not present on the list. """ ... class Map: """ Customized container holding pairs of items like a dictionary. JPype customizes ``java.lang.List`` to be equivalent to a Python list. Java maps fulfill the contract for ``collection.abc.Mapping``. """ def __len__(self): """ Get the number of items in this map. Use ``len(map)`` to get the number of items in the map. """ ... def __iter__(self): """ Iterate the keys of the map. """ ... def __delitem__(self, i): """ Remove an item by its key. Raises: TypeError: If the key cannot be converted to Java. """ ... def __getitem__(self, ndx): """ Get a value by its key. Use ``map[key]`` to get the value associate with a key. Raises: KeyError: If the key is not found in the map or the key cannot be converted to Java. """ ... def __setitem__(self, key, value): """ Set a value associated with a key.. Use ``map[key]=value`` to set the value associate with a key. Raises: TypeError: If the key or value cannot be converted to Java. """ ... def items(self): """ Get a list of entries in the map. The map entries are customized to appear as tuples with two items. Maps can traversed as key value pairs using ``map.items()`` """ ... def keys(self) -> list: """ Get a list of keys for this map. Use ``map.keySet()`` to obtain the keys as Java views them. Returns: list: A Python list holding all of the items. """ ... def __contains__(self, item): """ Check if a key is in the map. Use ``item in map`` to verify if the map contains the item. This will return true whether on not the associated value is an object or None. Returns: True is the key is found. """ ... class Set(object): """ Customized Java Sets. Java sets only provide the ability to delete items. """ ... class Iterator: """ Customized Java Iterator. Java iterators act just like Python iterators for the purposed of list comprehensions and foreach loops. """ ... class Enumeration: """ Customized Java enumeration. Enumerations are used rarely in Java, but can be iterated like a Java iterable using Python. """ ... jpype-1.5.0/doc/logo.png000066400000000000000000005326301453713375400151040ustar00rootroot00000000000000PNG  IHDRxsRGBgAMA a pHYsttfxiTXtXML:com.adobe.xmp 72 2 72 <vIDATx^nIv߇/|^0 !4 R QlҲ%Yd{q-jIEY([,/RHJM@ B 09tN/7|;W;Ү]TU9!*******(YQQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQQQQq*w PQQq{ THֽWQQ]u {EEmI\ۉi 1\q|-^4~ė^ 1ԟbyЍ*j{!N/kqBܽ։Wbq𩨨 @Emf_GO_q}g?6 ~~[nӉ?xR<,MѓX?^Y] NOK١RhN;hIᙆV;FAbێ3R B{N,~C'baND-VTTAU**npe m&m#aMrRbrөdd.u;sqX={x'chJ74ׯƧ~˗ܵ;RY:kǁBlt(얡˷䇒4V;vܵҏ?ؽę0hPSOŕa 4E[TlރŒ2$ O%e^ڋQ֏;8{:~CŽg/((KO< _1z\jg>啽%SrKJf$Y1` h7+oe`В[Sť30qe`8~?'ZQqg*ov#L/\#٥ܷq!.bD"PGݷh{彈iO{~/F8Sbw?;'N2k|<_@|] e~4[;Gݘtc|ϼ'V珩,H JCppj-`>Xp/C+Ux+ /caP?3Ћ5j~mVP 7z?~lY8w q6F1^h{lX^Cţ8wx~wcՍ&L{s//|\\ڏQtOR+-ŋIO+=̾-sڋû>%0- ?`q߱wZ @E[O|ZIqL훢[7nv;@f19Տ _ߊݒ Mc:Нb҉aLXYz VN zJ+B'vqṍWs.SЛCJ1F+e S^E+dtFv_iZcJ0oI PpSM[`n lK ?c#;ʐSQQzB?fȶoG=L釻ݚ Mx G-hKxO{1]Pߋ6^$ snt{ݘHY~a'O~f 7+;Vѥ\yjyl%ȕ6B>v=]~e%Ϭ o.k00U/=)/t(wETToĀ]L cdXX[B;囄.3bIn-hf׌Kꣳ Wz; &RbomYyp,wEk8@?zKC ߖfٻ%ɻwd؏)v%`Q3ygcS_hwTfɊ<\U+RX(En*c[4.Acկk& 7W/lGETT݋W#!$@;*GWۛ0'Y/@. Xkv0F @7:Wc2PʃQ&Xo"@z;ϻ)t[E '_;0Qz=~οNz;'KRDXBg9`VUyN8~C~'^s|姯WMNijWBSEldY[uf<ۯf%-->XRb_l;$֖m{#WL}i&EpVx@H]ޏcE) K^W>J&((!H!P`< ~񨨸3Q77wc{,KB\Tni.{v3{cG 7Y Jo (A#7;X_}1݇hr<LA,cOh4AIX `Ň"7oME?i[o3tU>M][2GM"Оe&3 nLUT{VTTIZhI`I|b= &n E{}KF/ fDbaٟln;4]+bt/Y)BZ4Vx? =yn hߒ%~~EؙB + :DŽ]Jp*cKMRNe?3)+Ei9d蘢T 偺t(F'ًPQqnXQQ&aH"Ȑ)dW_g&@鏐޴:gh.Ҍh>=7ORlk=KO$79>L_ \&PTw%L %2^;їT'%7f?щyϿGQ$+0E軜{CaohcE[ Њ㬌TT @Eśn3Z |J_X! `ExA"+}LhOv#2Sd?OTb+;"hoa~m"D*T8pEGQp)_ɫ<~u1u0P])Ra:w0p)?~ݳ^0 lk&-g03/Ky@vzrs|#`*? XO!~y/k0{|_%#Dž?;xv1^:8g?^s0r Й@#o0p:bz:lJ!7RLNH@v^$Q"?{ӣFU**dػx@ZHalPh}# (hwcir" d%m="O|:ĕ<'a-շ{y?Ƽ Wxߎ&|+_7BAfA2, B \nG |^?H$m}ksCGE 7xXǤj$.ӻT4} uouVܐ?}9g3[ʖ$\œ~B4 #<&Nb~f";o;X$ȾN^y/h[.L纲+2~φ?{; g3ܨ| ~mHzhP%%*~@{R**hTMƱn|1fw=OB+geƮfv#߈/W)|?q࿋{/;R*B3uo%P%їڋGc+#A>"sb&3S>{$')S$I[_A<4+ sAx~/wcw(EejPI9)u%7d 8W>p? VT @E[T˓T )T R 3b$&W[c UW'NX<=V\/"AcfDѐ'`%'ky~*E{mk9*c-.II櫂Y1@`AIIC,PJ8(fo<$P e?xr C 1T4ćuo'GNcVT @E[=qϱ߳=sv|@ǯRĹxvė7Epw}q؋1~񒤫‘H" -vY%mW|~̧zYt/ߋȫ,( .GЫwվ%{ &2\\ i]/V L3/Nnrg:/b ÞIQP@T?0ǜLEEEU**8މ1>Jfw5 QɆ8-Lď<8#c1xZ9݃K[P?[LIt3 S7"9E6cEUc܃NLѹQ잝 /^C4!Yg$3v4cF%͸W:)_4ε❊_鹱:8`8Ywwo?R***TTEُ<kNKpIdIIpJ brs=)q~w#V+{-D|~6&_PL63j#5w'K2|W0By`_Q ҬLy#e:mq?/-xoqCx_x`3`KV^V#v{6{ꪢbTT_ŒfDbs{|'w4Spf.gnWS܌, l({~sؗ0?R@l |^P*GꓲG:]D)[0)D4ӝIݿ8݉O/˚bskuY[_>k~N&xO?(?$άUx })*_6ݑlt?`?ߓ79t؊$opG\rF / y u"Z [uC?3yg@ !px2W*[ ]9|?JB)_8wy/mE[[W>X1@:ScmY***nBo1O/Nh?L6ejz^7OH1B{g{/7ȧk@q}]^~/>eV29S|hA[T(8Ca|׷&orb:`,?o,Y4 fkQ~ezy FŒϖqXv`n'p[^-N}H,?kW/_;,Y #}g(x6Č Kz񙲔AuKrT^GzP@g9ʛom[ybוB14E^h ^W蓭X0= ޠRg#9<+**^/ğ~<s+ojӷ|_9jߎ^_Tg {ߌNFkYBRq;;u+ JZz8Xցב{GhcQxXkS!~('$鍊ڽODg>pO b{;_~v/:?~>{PPXy V2XY`5%fH;*.ş5KJ6xS(AI@Vބ7jWb:u3jf̮q #t?q1f -&75^~䱭u yBKxPx {U X-ڕX~ߎILW&#0gTsy!ʹȃ򬨨f @Eȿ|!O|5V)7;IT(3 P(q} b2S MxE#H B!RO<%e}ˆ#b "Se( _1az2Dӊ3뱽3DgI-!>RF9eL&~d***)PQqbw;+XK2kN޼#afSV8uҟN J"}/8nE!"|INNcW #`I&9lٳmO?o+ B#?W'@i P&$͟.~E9 I] Q!H*#J J@=~z1?6Q?0X,7w/Kh^`G#)} Gd+rq웲r?&x!=|6f1f{%B|Z(gdӄp'?^D UƶTIO Rz^!:(|<3KKXҿۅzREEws-9g%YВ&p "`-h (AeTB ȄVx/:(߂}Vp߸xo[ _x*v$Ė{~**T6?'b+aʌ[^B<"XBY683 {2{GF+΁=]fǬ_\nڄ-㇄'_.d>rs_夣~gC&{3{P @[zou5@SDq)NGg4~k0P@gg7F@'_ SvXmGJ?&V"Ϯ}LxXtF9HA^eF `dۤ:1I6)lE+ u|[4YP^FZ(} >[QQ*oxi'._{<>J82c ]q-~#<# h{cJBNӅB'SIҧt*e~x"al*T\?  Jf~+p8'>?[QQ*o|K-h?v'O^3B–C=g S!+ʀEx׿-R="4䙢iC<& `lvd3y`?#.2S鏥L$'>PE;㘔/E{ԎA3i7tbldYQض[?n,xs&@B.Xhu?=&l3(~,Cy2ޟ(W*_?JXEEwTT {%sF'%5KG#HYGxZ5oSGX;* ':uETTݯ^wbZJ@])+/J ])|8HuCjV+.`w"E@wo7$xYG:h:f\7[<=8SvvE!?--ؽ1K(>P}οBU**ns<}i+s|OS²])΂1\>;,(9ۖ`?B2ږޗ;LPFHkX{^)BC1gGxi΂_柭.*R287񡇏KG**PT6 1\3~~nGJ뢄 KH %d̶yƾ-!{ A?"f¹  tS0> kyo l!9@3ȧ3'@EETTX`#8~ׂQXhJ|tP6|.hc /BÐy乔)~x@pA磽zb3WXtw?;Ǎ/{H_t(@oUNb?dO7_pRQQ*16&`.si8s2dLwboE!ď ;lBD7^،exsQ_${] `f@)-ЕAK9٥ `.?hV<aIu@ccz =Q|@^FihRc*NF;"|@3~ka;1>.kEEś )#O^;I0w8|b")`(czwD؇cVF!]>\tn9@>‰ifLy m)<] Ccw1|)R>G{ 7 ߜ387[# u4oz::jG{ؒ_DButR]\;;ēŞ&yk/ߟFnOBY,i"zsg%1D=Wc8`鞏%\H/Q~xߴS`ՠJ>K[g"MȾ=?G3o5&cbu(ǚf񻓘uc:ht5E{}/F_;{>/z%A,xp |y `$'!.鎄tJtE3ѓbaquRP$ؙ-7m[qY\::x+Qobi}?z߿û1]ЗI5:|W~mEX|"Owg_*1"|Sb4_t3~ ^6_C!gL٣ {߉q?_B ;/Ĝ^+Fѹ6|ok9At/ycݖB =|z[б=o t7`IB{Y&wF';[k&;hZb?_4WxeN?,hEE[TTfG7sB—,.t5Ŕm{a/ؙP¾/b'߳"q_ݷg=^-& 澄`^~A|O DQNHЯ_ݚ_=B]Et$B'zRR:ʛȽBa_5>.gEE[TTfx*xA{ѻka=Gg}~w, S چ˝8X1"E|tt7׎b/:2ۼg>R^gi_~@{2%yo))l@An g> PObt7h> W,D~L%y5F/EHзZѹ_k8 Ii8ޏ$K`,k4ivUz})]f FD)@޾hQz( /rёRq bv?P)iEE[TTF/oE^ݙDoCB|?Ms9-x^-۟sѹ③o f@ \+&s%p>U a~bN)suHQh/AϋXp:]qJy=P_H:?Ͻ Is;`Nq^D[䬔%pc?&w͋H!Dx3 hK8t0p A>=>H-T4!|4.% LY/$ )' 1I>_Qq;* 6Sz)rЏ R(D?>=~ ?X/)Û[YGRҊUMRllMN}޴r#H폀n`y?nhJ;@6 ͽ{:V&䔝 L4)Jhm'  ߳%?x< lTx7وc1K"mPQQq~~Vk{Lr.@^ۺpNL$-0 [ll ËV 4_$ e[[Yg}>Ž?7KoebƴՉaLU!ţMc_W**n7T6A|+J~>/;\^/mK.gzۗR?wAaz2D%1o!OmEhQP@ٽ(^9-) R4z1^xI[{@;̀#3:U梴5nObeTTT6 @Em_ⅸ;rљ9)cWoēнY9:J ?c_XP{eEmkE@,}y#9KV $(o(j(g$E;xXp~)FQl$^Y%}:}ox۠FrW{6NO~Q***n7T-^'?R 4fCzjf jړp֟gYG!`i1xj;<&"oNbtv!@!.c؏6k6S m[1nA$M7n "OyO"xP dx,P]QŕWtSn^;<>Pfޕ?o;sOn>>om|?A}e/łnt$;7HKS1Y-ĀD-Q Om(e@;c0O***n7T-'f_WN';,K+Y_ُf|'mE l)s_ݰrݛD7J`[ )(r"!fA<7fmr~ɼ,۟'ʁeKan'忊Ux -AO̬,xm+k{R|]~mE0>} %0a= i Y)@`#fH>/^ْ2Ž< [WEm.hX%P>ؿW+ݽ$>qשQ7^~Gw#t5}KO%xN½#m6do@zπ¸o/~\P$: i>OhGIYA`/eaLK\\L)4RF4? =4]ŸR|sba,3> N]sٹTw'!{HK(#6'~J~{^vn1(.|V b P*xx!Dt z6đEw#w$ GEE[TTE|u/~Oc'?nV:iSǞGXWKN}0q%R'ڱЍf< )Q@ D_PD9xھ"DloLʵQL{{co*e Ux/l>1RwvwN[}ϱN̷8׉ oOAٴw\8@x*o 43o0x/` Xd$3Í0v$L :R[\`N caeEzXXY;AP"***^OT t{7$Ʊy=&aLwbwS~v=ę;7!O:*&2 (l^,؋h iaOb*G˫斁IOK(6c>K, _$N&eI(5R"gŠ'O@EgˊoTT|nݐPߍk1ǞͽnoX¾-mD*oC xǁO!B34^6Iz4jӈtjie_|Y/$ąIMB0Tw,nr3$uv,ݢgqUxM%ܯKhg#f{{] i@;Y4"^~!*A]GA_H7Pdo;vo\Ҕpзx`n௣c%@+{CNR*Jl 8"qIf7}F%Ke,9;& .kLS/I˞#y*x0zfҸِgSIYM(HGiF{ 96#H<0Ne `2Rp2'uH9/[P%aՋވ{+p'yoޖ +lg i`6S5tE(hD4¯!✰ٌAz)ˌH>"osLW +8Whl5TB[>np>|~4< iZ.iOg]O**ɖq j{CaG=s(bjDNʙrl,7#VTE @mil]z) ףF {xޯk^egd9k[E+-2) $Vߠv"2T6oa73dq5L>,D:La,h8&Iy֔)(s֍"sdMڠ5Jz\qBҘΟ1=2sm,>%8&Xg0-*N_Yջ=+ oZxsP[_K-w4K {~֋I`_RuS,3CPRpn&x'40AT)pREMFO_ꦔgDB3& ] §,gM]MXw٧-N2KFIdEЩX9L/1 7nB^`pc`CKҹ ͬX.+xKKqb.)<** T-pjNf4Y{y^lz6cyQAde@ƀ{$d3q sWzPd::[lD'ym|%;^Zq̙|ʌ3?+a&x: QhPGfd@Sdɕ(,= /Wj,ud:9(LGp /30QCm[ qG<awTan]k=矍h,7o{GFijf?u C.B<C ̯aȥ_帘Gbbq W ԩ5NGٖ, ej Xqcr1**Pמ"=]y89zH+ȁQ7;mMFK`1d<T⶿ !P5 O5 MH qT޿eJz=͛!\ӆߌ"٘%Ha@&{d[ٳa-iࠂ'r"SMM!:+G Υ-gz<f<סF5{=!i+܍#nl;':Pm{Z5!"> 3ӓbM7rbrU*^U1Yk~MB2KvːuT5b4lexxhp+FC&~MiwK<[ 3yik&d^Y+"4n># qyN'v֡f~3ۡϡkRh, 8`I+*XYM G_qDg P =eÝܹ0_"^2Bcc{milfelEr?{(:5z_ /cdABp |8 xoă!C(~ YH>M:4QX1f-x.xy/EGA60+JHӟ8N8-.)SfMt{T3k;P'MnfFM&ĵOt78tfv]rouyd=8C ;U}f_No.kdЖeVY8,t.N,ӍfpG1!]}|GLm/;^l|l_A4T7~e y$#MΪə7Vbta&h#W:ˉlZđa}8~oقh^o!:lh}w;'a՝_i"ݚѧ#;;r;p"pȯɋL 8]*;8K|d(2p ~X#!7 N[Ƒ(:~$nZ]y94Ŵbt]Z. &#g(a}_=1>Zx'*q7n:9di:lF<4L=кGi?\G3 LjVCC ^vG@;^v47 $V>Ó4.<0 L|b<I KXs4ڋd({;J'['(_O 'w^[Wdyv5MG|j_cC.*` "Jnw^>ֆh_?U"Mf[ Γ"M>l&S*80:4O$OmZ+^iPp0|HK+j\\_^I .T~tPE@,@~-xO}Ͱ;UxM_O}!n0$":vt]hAe4iL,W7F %}hᜎ2ЂC_k7Vl9hfr /V09`K GҩpDs3~tx<:umϋ^RxN؝8,]"gsQ<:n7-ز2Zf";t Ȱ+ﺳb@cS-Y6rfZ0)-EW8mJm!TZ=)-`ny_3q;^f¾e{iim%MsGuApgѳBl-']+^4C )4q7~ koI^%}+LǬ =ĩHᝂTO|1v77%ŃIq3O(aqW9>.߾'Wq/2hlISlLyM 3jA|A[gfm]"x56m=v5cYp2M$7f<%SQʸ:.(١g!LԀ홮œ@%#3DxY,KSvQؗ?a&9`-mw[2T(L}K^}:p{22n# 8ys{Gn1c0XT[rK?> ,$]YK:93\ ^і`%|UIP:vZoԤe*XTd#ܳkH^P]Ln!M,.ljGkg)!߫ @ /~-.~<k@b Y 0L@:֠0J O, z Sɏg?g!,eKz 0ELa)]3C1ݗP2cz ס/rmf;C+xgh&79 Iu.:څ",gbr O:`k! 8TD /0mS@/V\ 9_+ͭtc Eaib!?)D",qRYdtƿɋ}pa8Tf&4S0 @Cݤ):Vd2"P.WlM7j">{>kp^CU*SO|^fe` x\dx`3L B'>-K>n;A'ۑ z 4s/bff5{;f1QP(rN7$ ȴo1rVb M`d'ŬBXXR顰4ʄLzkn\ndr'O#iv4-gոf 7p|KApY fV,>rL~171_Y{ x㺓@u? m7WLX( +'a|zc kLKX\&4T“xoz{Ua;{sp2!%5Y/; -`|aaܶh?aHf;FNO;}2n^)Pfo<ަwѳ лY*9C2O w&d(F^C * Cq(Mbx+We0Ϛi*(eN*e.4 sIkBt\b˰Op7m`6T5J tSN$]%aG|mAIyCAše+})@q Xw%gDm3\ي잙;@^M4L)9d~"!}}'.F[n|:8y6W)GRO?|*w 2FOu2x /?* r%rpbo30<yA(@\h%MMhAL*> f"(%vnĞb;#2FG@|ׅ":+^`1U`@^{,nD-p(6~Mf Q;K80%[ o?ck*ousm˴d-Xhfl\@hBRZ݄Ƈbu1N}I'O(w~~鄋4iW .t~Za~q51G_[,`[ J `'v⡦ 4,&x:7~6A"ô2e4~K^pŽStJvznzkIdDѤc1ӆVg^[ vvVqL8L4v\Vo_!=[t- ܳj2N9 _ʝ$ 1 ^n`CZn qo+i|}:l ^n{*ͥgWVg.$B>rf( @``23`ŧɁɁM1`q4~=vv51 I{+щy|61tNUƋ` &yLv]¬pY0BzjgBXyþ)Zc#\wIZW#qoK 2ܙ2bf-Hfp/G첎ٹ\9B +qAâSMMB6,%NAv[EZ¢ջbqD,򵟸eu; __#`wCpF uӁ): i㴇@ZnG1\ `TvӍm8~퍷pO/+ 6gūORl]|v[` Bو,h4aU,b P1Êg?f  |X3 f2Q8xN"kn*Jp^ӉA_C#P?t`FoٺNn~J1K9 L%k47* nׅ[ Lz:/7 4rbKXơ,Fs=S:Eh4ǰP'H·]%^x*ЮpHq'jD2 MH:^by5: iRJ)d$ aG,JԊd8@T/Q*+(](2X];K(dUMyb9L43.Ξ3𗡀|"AmZM ͓<T(H t>.o+co*!x^ɮ+و,0Njd`Q@Θ:|!P1B8 `~ 2f3yپۚm_ 'gW,p@;m Pxș+fVWɣbCD ~!p]i7qIʛ[:sD;V=cG6µJlsuoZ̭$^Tݹ߸G^<(nts1qrW6b )nyKm]V,H)ۏcb[Z+V+|rN}/  luqe=[?+ġ&݄mZ>!L*93G`'|pꔔIk,Fq4e)v$=Ơ!fpheL'V~SZR(gBӿa- =nOEЗ - :yx@J/| '>=\xPK *nGT{qDOO [cb Ϙx=a qhrT:;\LY9##~% {[WbgJlKx&!,K7rAVݙyg1;$};XF/̇4[0Upnl qL~PWbާk65d_Z,>Ozi˳XZcK6]k1`;XXW+g`?+O}=N>˓ Xod]_S;n߈ћ[˱|ظt\qRS}Hͦ[u JFG@תn6-RM1OmWUzĝTvNf_Ji=Lv(>ȼ7[@vC#\;VTKGwӉAl w.~+=08Mӏy7dnDKsQw+{_蟴AU܈'Dg̠_ t\gX0[ÆxF/ davXn\āB Ke[2}; Sp:ʗ9{Kd=lwÞAqKxCPXW.djSL*M89OK&zi\0w2zRD/7.s˚Kbos=y8ƚZÏ:1rl^^3~׋:‰RwwaODZ{/74pLeJؿkw=z޸w\lzU^VL jҬPi"̊jjR^r'wR+} ( ]khJ*Xss8 &4Y>eR ʴS)d#ґ\V2iz q2v.B^.ԚO}O򡕵qăj(g'MjSWU'kE!w4>_.^Yn@pYty'OxS{=P)6?/|עP>N/w:L67&6A\ 4M1 uv cs㢎 9Z+?9bk4Sx3vnx M~> ijC[߀3ES r yfb˲r2Y$r`T)!vk{$J{j6zK^Y.t܋h{'xDB;/?c1)W/ok(l7R4֯mDZџhw/zK^\y W]sk1ڊ+<e)ʋ"nSP^uzٸB,;0zf{Bh]dKKP7:*~) 27vV \*AEՃ|#M/-wsMKٸ@L:ꇎҤg9M?jUxs_s_?uZzk ;i:,tA#@5d](ub̆3Gom]~_^G H‘Vy)|ΈAnz! HR-yxVYx$a_L?i{o z:i 3.a;+AC@L1ηc[u_X {$5۽~碻w҄" ϻN>nmmǍkvPQ\zy=zN8u:?{x/HJk,bڋϐXq9 {c2˹AP3`[|,B2!݉b՘?jhgm~~R{RTΞ ]l>%f)DBBJ\G}1'7f4'N$"[J(k~^oNqD!b/n("i@ lt)m^|~QoYp *̉2{ ~Uu ?qܳћ/>]V S)ȥ֟ ;/~HD_Η76H _-2fG: L9Kd-d#OSG\N{3{!Z9GEI`k`̥2r1KrK*ē2Xj+i{ [M~[2xw㳤xk;B,8,~w%Hg@,q R57Ь۶^8$)A_Y /Vbgˈǵ\0k +ZɗV?2ё_"9[ J.u4;H🋍ѓ4;wrK͸΃*7?C R/߳nHGҀ6]SM`n?RJm(0qg54֜Mm<Ҵ5y0oӀ%ay?\?p#mR4CSr@yxI> B/Y`/dr6eb}q@Qf*o\oic_XϗA1@d|9 eGzn33noǵk͌@ H3lC6'200LJySx"CqȻS "wvbG3=a<*4O"it䧐K&Qoalin:X Gn^YR zCOtL-}ol]8sFw9I`+bR&/dv<_0s0N^ζ_^q>w{X9sol_}Yٷbq3zsJo_9X<Q\nYn)Ʋ??"I\GhXK|N[f9o^@! zR, eP6Yw;4[Ɨݪ;!J8k:$ _jnqx,5WeW-F24 <8!b8́\ $,Gؠu] xPn8߉7F~`!K|4ھx0=p8 ,$ōJoq 83MkL& KEȳ b3!Dٸ |)n Z1KΞ0F܂P3wN4?Bl/'W]:_n6M('iBo<>JTuqb|!?Dz}K_|l|!Nؾ_Գw=^|\[vF;;ۻCɼ~m3NXvy7cnd-Xҟ_+2c~G ƕغvQtb0[/wxo ܴ ӭQqW HK|.e" Qj +x6;s͙]\MFAĜEk+q+$+R ϙmg>ӓ(ؗgBQb3i3y472_]Y9ǏCvI:Dd (Чr/ ~dnÞTY m+/se KՐ<ؘQ 8qW:tAf/Ypo'\f @.jĥ[02H3C+i;VAt@KEfvq\H#ܤ%_\aϭN=˟߸9ggqje9Z*07y3gc>מyZa#)IX羸S:3/8Ul+i"^l~ڲo`a-n\_>X\=k񣞤XXWײ?Bؒ/'7'Jt*A% t*mE WVUX*^1}&DZxx`~%yOl$3Z6=b/z>seAUncLwo5)l{,Ei zaԏlmt: -1y +˰tK= ?k;ɻW`# fǹ Ov3ݖ:n? }) ^頬0iAd6uìYIڸd^ЖăW֠(>^q@3ջ|\9 U /yEp? q;3r}a_Lrn؈܉ igr#TKV [F~UxCp[޵n^ ߍyњǴS/6ڢN/zz^^D$&nqM桿϶ ϙO lL{H'3y6;kB0?Ղ`Cx9Oѯ^"ec_G)t-Pv+Vжbr P⺏j5)e*@}g8 Zƀ4 i~g;&;'p_[Svkfa 2QgѭfZ{ ~߂Ϊ$?L>LUU?/4lJb7K?VlmH#Mp r ŇGHJ[`JM;V!\"[0 z&/QL utR P^0uEVQ$QK/JȮDZ͋b,˯~K1E ה 6ַcĪ4vwH)_[.鶮ۈ.WaT&TOI3pDOXTYUE)2j:a@c)sKpդ?< !4GVF,1Y۝&~AhfϐtSꬌeyp5"s }:o>ŭ V6*+yvSva&A[rej̔hԟ}cRO&3|O?r`iO:̆imt'y+{eJ HHf^--1#U(BI!YS^o^X- Ʃȡ =KR|*HT6hF]GoTTx"er8}׻ś@eQcʇH}@yS[4[N_$>o\9XxCP _V Y=(R,]:9K9G_1._zBZ8K!j!",Z X J^fѡSϯa#77cckQ2_Ȓ_ 7B~-:[0}70 Jޗ'cR,)ˌ'f&JDQ(PRf|]dC+vҁ/o2<{k/<˧^T'1|ܷ6^~{D?Y&)tW\3wV;iŎiy)DZ!~]1<,'ݩ2lU(_IT82pǬ'Q4ى5)1]W,#uc؊w1˕Ҧ9nrO&WnyMIgY`Te͹|aU刽)~M?JEtd:z$,d43> &|XY[2Ksf$̻PKLzq3(,Mf}q? R'26>(K 1eNN{9%YoğM7Uux5G'Rl&7v錀_,/C9sxG7= g< WS%="خ-!uYcoi}csO<yq*APtV4($ yHx0?͖A$0yFys愩*9.$y 8ixTvb3XfIf%BAeCS生1N-t޲_;_`l]G~ ݚRq+%~~8ۅٔ &LREޔ eu4msXFHϸ5Wu7gc0K!?iPR0-qୃ'YF[l"F13td>s1sFL P28b)ɟo~i~>V2Or g]u!:T'/5{٩qZkO<Dž)34շ#Ӹå̯ |=ܬt5)Pg/(Y>9|ݤ ,-4kd>p2JA}Y'JԾO?k+Zцu[h)3zV;ˍKCRa,'"B68/گz . f zŲ$aɎrkq/hV3B|YNN(𗳧vK8k뱱Mf430 5mlؗef3#|@c^~I))y"nC_0hc{98ʃ@TP0&Rf,U oѲ2x/.~VVOާNb#ɾgJmЇQ?cvܿ@Qt{)%uV쏦7RD=ũ<܎J6?a:TޚԵb;Wih8R? ^-B:e5mǷPpYᕬ"؝g$8 VXEBH3M;usy R/u!IpJ:GCG{&x#0TL,XZQ)5mͽ,pg;_GUn{*.|S( ]dNCbi@}E!c=/sECLnq"9$'wOxbsGDPeYY&"oήBnSWUJ#G||_#,86\fL~JzqxaahXvv*64jңQ祿G'T_`my_6$Y MK(TnVJԿ[R~9F$C<(DJJt|Z Ms9)wbGxQ>ku,r6 输t%|T|v>Ep'wntpNu :!d?>{ucg9ZNgqWxr_‡{󭸎5+!l T(+N0AVIfnrcr4ˢHbuIHGn޳Orfˬ f`A;i:4(.8-Gg'#x>ziٸf kb]%?KMY^juLM;ƒ=Aoz)t9\E+܃B@l~#DWs<ReO9DB/=!rjꯋڎIaqod&xW|t#/0hb\y&_KP;|'IL+ ܼ^^_f̀ΫO-g~]"xӅ4}+s_!?"e Ϳݢc"Q){ 3j>3pz<`FhրD Qh4^l<.1 Ve8?UyqA,vUnG $/4%~J+Js^;>˸f ƒ޾9Kd\i~O̕gފ'LȪ)\*\*sRr+>}N)Qj$a1ѱxV7rF@կ~( n/Ne\IaGЁŒRFu5~M&cr mcʚvڔFS?kIX^૑ o(ǵ W4ؽ/@1b75y]?ģSf(V0T&+9C ^kdvIU mFkuz~.500vbOw~n Y |(✄ȸGu7?`,.TϠ7HT3/ʎpfyn{~s¹+_wu-xD}cWgQyG?CC{ sG_r;2FD.Nے44=#S<6ܸ dUivi-~咍dC@}Cp %݅e&{)B?૔?5o壨6RsQWp⢌o(G} ɧMsk 4KˋVP P6Ae#.' 6,PfP}䓼#"5?°yo{MU ux3Jw}. x,ҁ%yC*`8W6ؼ2[țGrǔ~Hzp\]JzX4\myD"@t f*e`[x74Qs T":E)k[Mi<)Ǻ0YgFRi.-v*to'6U ܜHyZ*o'@!bOv:Ro `yq\RP^#M_G_T*ܔ_8TpNMyu`7vԮi>GqKi(,W3+γtX(*ܷDKݎW.Zd~~H+{k>_O<ʿtVJ ,u~tR6/ ..`+Dƨl%+\?Şu4Q{G~2CUn\}+q ~!%`hinυrs9#`| -fH䁈 @W7 wJ{A2sZQ>bo> 3/ L)6DB?|61Lh2a3fn'J*C~׃j(g Mנ..RABȥ@ngrunR B7Βxh Jloǵvzq= 9գ֠.㼇!K=#@-9>ґ w^+3M7UĠ4PZdO|h|@_+6̂F7+|s^#A?3 wFaN^<"`cpoGBGȀDGݷǠw+}F@sj=Йʁ|ʮȩ$_f GAY VKPf~WE/ϽS r-s&㸷w)ozk?DZn?NZZD:&Ԭ,l&ڰO~xODASu\+q(sREECq1F\f F|Q2Oy3Fq풺jyf趌[f#Ht4E6ܲb:وgesw t)V;Vb%!7ؼq-n\z[IM^Ny&h1IOX'xhl}QXtZ ym0Fw9hGC[^<្p+^?xKU(C?靳 |/#3$:Fl쎢hėE_.l/䀟 <1N~d,pOL~7m?xU6!{)Xlʓb =iqtؠ)he1Jr(:qT7QFP%>Jw8x*E[$')L&!XAOc*N^O{nW64 /6-,s(k26W h:ru 7}f7v-w+o꺴5ez4u x⥗/.zR(K(u/=9\ DY]B)~,e0Fu6?3)ʬr{7-=xJ(6((":Wolƥk׭ţ8[ΊЗ^r 8zGnS^HO|ށͽU`P` v;wqwdt=V8f&AS/B}#slج{J;MFr.+a@>҃@ث+MC#0MQ?< y̥L|A/;VV2eٹ)TD ki'9H,v3''‚՜fNĎ +fӉ7\n/fݲ=!_uRDԇ `#cf\׀:?74-A!ӱ:ͫJ`v*y7],GUh.cF: VaS++i!Oie_$(2-XٷBN"~F$5Bյ欮@?a`EQں+4|`p  l 7ҳF}5eA,l:xTXy9?!EnyH*kLsY(>cqͳEUn<[?^ӌJBWG:eEg^@K98pAolŹWE }o o;(AOa`-ǍA!-9MG>͍ Z0O{"Gr_Ƽ*-ű,7>k) B?u-څ.Ƃ񂮏JasINtЎئRGA BXzeQx@\ [4)VGz,.M͵ ZȀs%̞: cюw闘*G$ 3ff>QpI{6>l^)^}W,I:ڥS;w/?6#Ҳ)5Fjc<ج6T2NnPO{V?>#)h[^0 Twdw7_е«}]+9hHM;i n?磭vUMp˿W}ʂ 0={&KYBč }70x0`1P32ㅋcS3&^S250L<7WawDEk+lϘYUaz: ]˝x',mG@!g\1H:o.LMzQ>onahqӡ[HE6]s M ru3ߋV^8+1b(tuֵgɠ-={'?༑!E޲[PP8CL̬Y Pj#bɳ)a3_K %Fm7!<1r8;jY2H7nCPv|$?&%Ï"@<ȁ"ܾ-AD]? aNePVV֎K5Avv.6o X;yʕیYuN9djxy|'Vdo K1Lr~ ~ ͇ +˚pܗ~#n\uOo)0#;~1|tt6i5Ms/[yoЈ %WVvEUL6Q܍, a` )ҀLǝL‚i!M.yK W}Pz@MF'ɦ?662zfaaܗ:,8Y3?86ĄG(l5oUO< ק8m2۝7zvsXJ«1\6u2I^h_Vu _oRJjCAqZ:> qb㗔wC}hg{32HV^LC?=RʃBI2^mMn* vO=ň%45홂LEaO 0՗[EU % kP}cqoS|=F*dJŤ^=C!TךkuSj5U`0YSo HѦJX–eSkӘ)RB[C61p{(FRS30APl',W;!2&Rx_i/iTi7(/ pmR+/ϸl۬xULtUO c61uAo 4c+eZt{W_p v̯PB pg]eAW✎\Ƃ[;: ޤA\MStu RIqg\2QFn,u֡jfNY8xr=DAYHYb?d;_?_on|>D{,̱wRߙ뱼z|ֆ}(] ,i?ˤ |+>oGDQK$aE޿J~|ty( Im[GwTcC/J__{gcG3~ޒK\I:ŗzIlWL!p$c-JEe% ߦ 5|oK Аi4p6;Ҡ țݳ.96n:  fF_䉠nqdъ6eX$%AyDr/QXFcUo} 4 :M} \4C&^ݜ h̀7&#lxáARgIBYL3lD:ScAPsXڌk xEv<. ̳=,m4ua`Q|ga"M}nFqEڹu h7I\gn3ox%1IQo|2.Tyha?뼷q.捸Wb3.bکؙzXZ"㱄8%W"u.cvxQRB7e+S Zlś7:1fDKg_mk7˟ww9]A`v+ efG}n:#sxҠ9s&GpZu)nk+WtL05|;)ń~B@Ǐ͑}] sЋ/=o0$MMJe(Yh12`s\_^| NFoq_O-/؝o_`畺DrFyS=duC錓it. t HI\$O%$dE*#񁵯bIԢ{?~:8ozlKyd%9.{;qL?Y-Q;̭7I^k+Rdp sn{H\ORqU \zq=#{ 8˰?& Ї,QSԲ}F( uI?,,ґ5˿qƔ nmMؠׂ*%c5?75gn0`XƜ`; ;Kg/?텦-V48X=ήb-.ōx z>6;;CE$/ "?O,+ee et9젤.R;eѾy+32݊ )%3qGyk|)vwb5ٛۨ蛺MSHA fR@?[Z2rҗ^'c8e#Oe2iv{k A:Cup@ 4APYe5@vBDC\bmf=h<\"`eA}r %l7>6k?w?0K?؋?]KrUU=Xz9_"9=6'@u)fV!;,k;=7f5?n+ʩ,KN&ݬLBiT;3hL8TJC9U9'{³~I,J);< 鈮RK\NWn(VV*_Dk6@g+&8렭 /`masZ]3/^x {ǚe =#a"G`Jlx?P)y/{ CЏf|CzIVD X1!WQ_9\|X@F;[[p @ O#{氫T?.jֿ|ݷ; _x:Ot㏿-~xK1Xda`l"9XLR+ 2ؤȣ:+͊-6KU< #~gSf\!`^(@,XB~ Efnq}gbwc!6~c_R%=jE݋Rp.*Ϋ46㹧Eߪ+WuUu3 le,p8a?1DÅ \|`80cglalp:-#ےlI(LN==+Nu֮]5=~^Jߛҹ3jݠ:& 1Υ<ӾiEč_\' ]r9/Є}/#|ڳbRh[RF#*f:A8|fsyҸOhjFs'ßO ͟(χ~cHf8'[nXW>a16zm~$7"V sYčH!\Vo(s-L\ ԶDlّf4?HR5uu$3)i3s B<^Gª('bE)+~cڪq`r$yJkZ^J6wYR2kDwdLF7fz #26n,c_W=ɏ*r01)#9)Q22N'ėO.X"Y?0)*G8A+͎u86S䙭}[1?xjfNaW\{Ժq"xc`0b(\ %xn;O+~>_xtٿ7VN 1W4Ot*PW7}oV$x:0|̘ '#ĭXu>:6V.*^ 㩩f=8:5gtQ ZN6->[Q˱s͙ycf/ySi޼U'='Ι˝ܹ2ʟܥQ|U! tPD7ds0>? /rklys8'Asx&`c.q3c¡.v :_ Uq/x4A$)E[tyN`L+#i`[^^x\i3ѕB_~ckʾU70$g($a4HuW;@lnw.}R T$_f}!۵i jc"-Gq)o }@ &!ݕ^'QME\^x$Ѽu$k4z`+.~$bs]BtF'O\}}P1Lzޤ9193<3Z+g/ǎGCq榃aHysmaL Wg 0ϜA|?hdk& wm+aWHsklj3lmzؚDi <9sJҏƌosA}ޡ ;#^^贓8ۊ551~ꫮhأ:4JSs ^ qSؐI=;P >R=kn=O2K^y܏TwM_lHc 'gs2/0`UWr^!0Ps :R։Zp WPr ;Yv`v@m[@3#o`\ّEa [|)`Sl#9LST]~Ae!7#os<NL1 z1uӠc.N2q2]'J7:ck&ԸT9–;~2&Ԗ܊z{Oozk+&FOf TaYP^k4s{j~.f^;ظ{"d t7 ڨvum菂nT ~b~tt俹SGT^wVc!=_ڋ $2ئ僉7MjRxEoxS6Ai( HX V~ tFzW+m;@nx0Y'g:x>2B. aT@ۅSoA2:OeǞxn{}EŽ7ߵhGvRn쟔h;4u+fy\WSOCOr0P.S3SfsKkgcLy?8Gm/8t/alFX|d,=x{կ +%YI 8.\?h٪Ys6'&YmYiiΗ mR-ٞl' 3@砍J^b1IYA7ow[sr|TY'5rgoeJ?R&4m45@6Z}*C6ҀYw#א.@iOmW#Ln!&gf?i~€>EYZ*.ӊ`qfcT hd<% }g5Y?ZR17эhŹ38܊wԍ27-Xܺ6aZR +z&yN30'}"֗Ww3gcݷnyCw,y/N?t,kգb|v] )9ODvtC}Z`<8V{yV0;,L(`,gubS1 ʹw1tK^U.̃b$!UU-;/:-[\]Q1{18bP~+f?>ѹa\ [ӶM#mVi<~ӫcsO{rO/-}szc~|VJœ2O>:o8rh*&ÔO f#7֡9o*i[>ſ~bl1Bͳ)u%ǛDLH U ca[%B\V3H<ɈGOF|=#0Rk+~7zqsp!l#w* |}S13?+.Gőn#dsna..>N񒝜b|jaQ" HʟXukbvz6|τy44xt&5q?vC2x<<R!+yՙJ/nWɜO8{]%mPE[ m8S=e8N8z!\09`t'ZEEڅse_~vJy{0!Ua#&3X\U5"&b Y* ʉ"|yX :=-z֨a#a |JRƷ纙*v.;Su@Fc"Z7&xYy>U" <'w׾m+ooRmlǶν8#LϏOiBLoR O|[Rdq /,򹋱Ϝ#S gĸU$4[}й(W]|C|rSdct+/Z#g4 @B^!iˌx0#7)ӬՍic ظΌA+'LWL=2*J 9hZƀ&C?A4 `lt >3ӟHc>86> >7=qǭ/}Gnΐ'NHZexcmiw9]6>x,>!yO?AhH<#BJpq]g^pV^#}䓭ߖ2k̩HAQ)싯o~}~9 CL Ofï\P2y($J_:eU26ͤ̄bc(;H :\-h;Eir w2o) y_Źg U^׳$=jNXs:V;qn<Ԍxr}4X|Y`3NtPtЁxɧcᄑqKq+nBҜ,+{bcuC}ra%̫Y&dƄcϠ8۽P 1̡h@o".8#KX! b܅!/-+WFOVXi[lr Z8AB.ՋMizibrL[!>]W%o'YkF{7@,q'y,-"CrW3m Dgv|#n99̗I̯)zsNsuez^.g\$F'fDf] u}}i ~" D)1M)a3vp?s? {0㫓H3Ӣ /+P[IPЂUIȤJBs݇*H<̝cH.F@@+x;tFqؚLC7RvO} #y )K2GZk꣘?:P z{^*Lp}OǡۏDh}ž™ 1g&V..wjCq+RxW%Eh<\~8ɇη#'y#z~|(CBYq渲29xXY 8+jt:ɖ[Y+WC[RvCW_wss xus#4||o/\[9:faGI2ܪ8Ws+㹵eՀ10`΍| $~PJ |dYM$T-u絜9?Frzp>ΜGyG."#!"s{bbfvO Ƅ>$!:f]vyಂ P "8q An Cgl >zh _9Wܘ.n_h^Z47|qD+Q Ѫ SHvJ{1\7[êI:A2:N>+ )n웏3?)e'=u2zW#bvaWϝvn}ɇCV>~䕪 wkxqD|@9$A8Df'o j7<0P] Q}58yOX r = c%C(( ac@iO@^(;}SAO+ B nwBA燝 <7˅᣷+Ν_t؏׹Gρ]B%|+hK4:o ߷ɓcNHc2 s%1 -gTX?ʎ.#׹W?W=rszpğhoSB2+ %gEh!z7 eoKg*䉒0.H,m@ A,}(wۦ?*tobbIvHK%[qƌ#׭>:3 Ʈ^P{uEyd7:Q|Wܺ4~9 ^;ҪEJԡ}q'| p֣h[t|-,X&rЪBLjv-n+SZ߈}8n~+)# Jbf^™e;>pqƫ|<3qp&szc>sg! =>^ 窯_uɐ6^+=+* `EOOl9qڿ7o|;egI^gXR3\%?ӞSԴ=]h>x7b$.GnQ=:ךsg^ҳytŞyn̟ Ges򠑝*"r}YBI\uĩKo97 1_J !^x'Qsf6V/O<@z2RЧQ`4hR`fCY͌F$o[rЎs ]P5nKH'4f/kl:#Sٯ:ϊ bu_Iҹg\Nq!UC>.x/2ԕڟҪ )?*;O=Xjg/>gN<qW\<{69O=W_U 8q+_~:]'?P׸/KFsb,Kik[[YI\fc S/u8t06[̃8u:sKg pu^M}7]9gJսK C׾9 uc@)/?׿3ڊntYw4y8`1X̜V#?O| sy? ci1gW/8'gW([W۫p>\0 }x5g9~=N_G{}1)o?2zQ2 L_HEGA2H:20Fsxo뇾GNmACg5tH, dV?6aLGIȫPӁٮX,i T2'될냃Z HmoR"dxР0jNT$.jF8r-Ic :ϤS@zm)u)qգ\LLO+kfrbmgĔVdO0sح͍8/DO|agcm7[IhSzGZR˺ 4% Fg%'04X#K)\(<_J^}}+Z]f9(¡3ޫνeN1< !\ߣVhJ=7lKo?osMC޽ҘSfdFp7bhfno`A,Uyahn}cfɸ/|w'~i]xsE?^苓|b10=gm)}c@/>ᾦ!9 <6q@.ryi@<ӹ]\8Jj\( )PF9S >z[隈yI_T|Vv[;xqE"/0+EV&5r+T7>c2e)@?NY5O0yBѰ }Uq| ܨmhU¤>)X봄s~#Ǎk3vM&Z0c՘QsO.T34œU_hsT!>aB`PqhD Xi /٫e_qqأVW|Gdb4 '\hXBRׯK_Iq2s/Q+Ց|'`> }_=72섃a4]61FR?7]")G$[&w/HZY:Go+7EVɰOMPç/_ ~5o$c O厐ߌ9S~c/ Xxs^ g*iS*\<{g+zLPp ࿽+qp~o &dηs$ 4N"(1"Qǖ$BEXA2WpTLW^q($%eOga/tH q(^&s'&y]0ؿEK:GVkn&b_ 4JE³@zʅƌ[>b|3y^.X8=q)X]^Պ8{Tݻ_+|SO. >sX^Y;;Nɀ3'N?n%q[$!"[NƑ{/|TOLlq]Ŋ$-\'B ϠG`ЎP< %,1>wVW0mOk7`._W--Ѿ$Z/t_4A9_>#n]Ixz`zϢVDߕ|GC>M\?ctXبW<4 %5q`/S[qbW2. Z8<D،x&Cn&D q*%/ l5kK]n챀Rkkh;V)P~ʮJ૬S ېyҏAW+3 +rk{"$ê*INcIH8M-)}u Y>-ht,YS9e㉼wcmÊ{RLL`h^qR^1s; ]vcQpho%o9|q nuzv}L?/šxqZ<46ޛʈ+sbEάcX\uT'yLw($hKJ .tV4&k~;ءI+=i>u8p[mO?B^QdxXxF(q=#NcyS؋7zWȝ8<%)stҸ(OMs k៾'~'cfJ]JבjhVs4Vq>BhR%RA/C[ AyVD* +26nU5Q7~?''UO žTtL#Jnw}p3 R( 7"'3–!}(݀^yw N>q* ">_r `>'o KcDf.QGce<$&ȓ柾FJU&LVd@}Cp^Q _@C_'Ɵ+:yg:$-`*%̽C'0^!粬Tª;s8_cL簮,QT`Wׅ,8+Z pNL 1 sK&|]4BN-}yjտS{q3Sc~:a8O+ߛo%.Jon꼫iS12+K9V9޲g$)`OqX';~L̄V|9cVث~V2hjtz'Gz(sj!$VkUV9u~pۦ\G43M+w&_]^9O'Mg-=|\3eWR|r<0 yp,ړK|mWUuNM(x/{5~ |9uN:;F=5q)^+=<ϥr3\}13suLvU(2 n?*208B&ӅWn{Ƥ?6j@!QB0]?ֱAqu`^K2 [~' ~Qo5ؘ1׼LM}LJ$]g?+nl4֦O 034r4d?y:+<يǞx{ss݊wuu%Dž Q})V(o_ġnDlbrn&q4XLNO7m)М2km{^EˌS_6sb܍V7>6hu!}rو Vsp1J[k_:sɲrrsOOOY3VpPiH80zjHToԪ-wӵFNipOczW<GCXGڜOL8?ŷ1QH][^܃(J5\E/'c< _`W.Pb9Rg_)̛ X˱bp˖m!|S7~] :շ Cf3&Rᾒ9dLʌm ߒ08Q])ڗ6 {2L(GSA8NYQ#|M] ;K|\[n-z1mzj6N:c.wg?qmw.]PN>la=1bC;{ת}<Οz:fgM/)}X^ZW|@uuzڵ'bq{vyҳ =ϷKUB>BP+~ )(vPd4r;\߷`5ow~}|ϡyjfZb#2粓 =󇢗c۟4z>KPq r-6ES$HH`NXsLrڬo -'cfdm4(CW%—NڹXkDdXU%wE&b;m@&b7 qfz>[r;S v?.8;?ɸWL|ǩ: o?@jirETe$fZIY08 : f L/22_`Kcx3Ƒfg=c^ ģ{_geÑc!**QFAcјolń7XVW}i>>) X6tyr %*s>( ;2{mS_6VbsD5F>''m3BB@H6Y`h\E?{bMV0o~2Ɉܸ@)"ɿ2QܱP T_sAW> nCid@򵌭DQ{7xW/ÿ=ʵqE~e82K˝xU+.+zU{Aچr7"`p)9qK^󦘐05~ܤ"d]}_+ϻ&(sҠ>>A_S꬜%pXe)JPE|뗳1>Htޱr`h\%8qz5^.Ne`X"z\Z6iw f+殏5|!HUF W\J|LLziP|k|+gױkk T 퐀I/p1>G^N#dƬ9 #zfL&cw PN4+{D1NR`q pb'?w& N1gϞ=z7.vTSju#ڞpi@`5hC}"j~|NU<7u1b4 3tN6UJ[;ĭ8}?)AѰFr t76d>pX0d0/[_g{^yio۱ L/XRg?>rs9FP]'Ow4yU_ǔ{'(uV,<Ji)M,)U{|Lq`s@ &+(1+[߶/n^lҕ@ǀ1BL}1 ȬĊuDmJ-u.K ?ޙegx'>19Uwuj$%ťs|.}:Ǎq/2n U>-yVGTz~vqE @'9w~[qkh!"Իhq:1CRH^L6هMmhLvFPg͛6 1DBFI&.EZ7I%asp)WF5~ь;_X> @?n<2oܷlp*<︈f ':xZAI@ ̘/?H 37Ч)f:p`쟛Њ'hE| ^$ ZtNV/fϽ}6Vr埆FWMQpV$W~'ҩc%RR8 ОU4n5 :YSIY33uF0ZcП3mV]^)8^%x8x=5z`Ќ ZϒWh=WtPx{]_W66?31"f7׈hx pqbe.Ii!.n€ey@KM_@a^IPar?ϠKIDAT4Jgw@j+Dy(̓y:|0:!W!aHΊF90o"LXGbeDE#t$x\Hv:Z*E GK\µG_01+}7uVLƴNx{- 21\.qo0`~9&CNv\N5|Y41E,ƕC۽hW^q?'ט%$PԝO#1W:!e+!}ʴW HTxsT<: of9cœC޲WOG R Q|989XjJ3]!Ͼgpx)8d_|]~k$y%OuRlOeĔ} 37zgM_wuqP spJ5ՑK$Rޫ)T6]|SG8. jCI4&7}t֖bscPS5Ma!_rXO -~&E_?:p_ Ƴ-+5GcKKK;_3ӱ)|>D&|Hyr@/uڡ~/ Q*/ A7R]>,|2.2I |g8̒ |i3vA-8E^A%RXq|] +r'(TUNpZֻU|Rкid?5p3)0aӘ'GZ{R*hԩAnB@^Ճi'1l^{X7/%R}3 ;Q_ucA(}+p  R:p[ @i,%dKq|l 8qأ2 >~q̩C CȆMs/`Q,v .خfۑĒY ;uk7+b^JeRwK:¿lg70Ŭ3=唯 0|Ϗ<#%| xTƗIm.ŝo19kyf;Fk}."//O#77: ]zpׯdLk*@۬V[p#ZII. 6@’ ?A>thꗂ(rb 1.Jat^q*1JF@IyoeQJx%Q¼>4 X֡K)K/ σ6Y%K֩0Hd?.(GmD]_.exDo)N|sqYȖmHe_O/H(mLJfEq+}|+OWPӊ%'/S1&q brR2F`I $b15k{sp`im3nGEI %7՘y 1WRT(PӔJڊ3oc0 27rxm&$FRs(-doFi@/$̰š?cqd?hiJp_„ ]y'`SHĵ֋ CV| ). o2 1ա73Q; \ zjLUr}A,M\`;,fG5~̈́fS'F$&cs鼾VJ9aGLhcu9^}U'$%Y_N󰸂1Xdr1 G8d#Γ{oveC?cfz ~Ib9( U8I&2 'R@!_8&(w|^&@tk  .,KQJWq J )Q#]'(.q`#a<}þcأ<7 qypAV⥪1&ֻ^QrcaXX{9U];SүrY+R3c$p‹X 1rI nt+u4p͠v1ͻ@,R~ep% mpb4gU|ԄݙXKD4)I5`8Oon@ʉ;sؿm_l@N ~50 b&NԆ&.K~.VM*k/%pp8xq)ϊ䳟H kPw /=P{]{e_Ϋ16)z W'׿][VSoXEl0x%Ir$X?<0?yܼJ>l5l4hۛ{8FwnC3^1-VZ42s!Tv[=aoO9zp̽~1?7n"2LT(rMap-LDITbu34uBE1^6P4VBz9:ڲ/$8 d` %5s DB® /)p.K< /-=Ώ*9u<Ξ:a%T ʦU,T V|o(;VrFA=ύw`(w }ޯ4끢y}r|hM3'{먒0<9*l=@1wmɭ4j}[cϾk"ncd,='@J,/Μ切2"-mnlǞ[^Un?"Q?'As1R 8~d (O;eS EIjIv}^mn@Qjw(|dAD$CIۙZ|e #K i@q;:g/6d p@8+Cʋ>X䝕q1h$FD5Napr"l3^x7G%I|G {Rx}Ny|Kw︿Lq2[FI#sFLy9:h^c|1J|dݻ 5ʹh-%?CFec5ޒ ag C2}s=|VRe +| .J\(8%O4xr\XxƅBO}^K X>O!M V PKgPT[l" a2'APZ5"ƊѰs@@|T]W郀{,VSRND4"ܷ0 2Hx؟0}&2IbN7 ~K0%?WQV@|w+:j rw+ڵ3OxZX|Ona9_\#39nG3 04.oC-V$c"@l ,n^$, l1&VVNg!0xF,ĹꑲnH`m|~B{E~{/Wf~ʔrf ዂFĮ˂ "92+bƫpe#SG>! J%?_5F| TiTx'honw_Ig 'C̍G*?-) (|1VxMq.#5dtw|G_ юG#sR& 1P) h '_kC >έ7}!Gecp Y+{)n|n0(b|ckpu{KI@xj3^Niq=ʆ0!\V@7G0AH"^V&> |(e|z6&y Cx7?uoMiU4equ;S.ncM;4R8+GAH^YSU0VT0V8:Pv$_dY!pE ȁd9"b@/LPȀ'Sʲ,J -,Fy%S8km%*؟,ƃt8c1+OB˷4ӿp?11 Z3 (Vt2 nlHj1ֿU<-@ɞxoz$1-BF0hfV@%r$9P0N ק]qCx,Z=CmNÉGGE g2/&rۊѕA!|rN刑u%<0g ˃#Q}cEsF_ah|[x*ΟiESʚAX; [l7Lr7x$ lePup31a$|Aˆl:N@+SC8f% I0^^X! a f\#jpqL'TIAt'`*> ȗsSŀfҕ^E-7a|vlb$c9G]ʟJǦ Cc _ӷb24E$3 J$&>"e?nՊ_nM _ƁyTސQYLŇH]V↦$DN 9da4͸ZƓu(ŠaCTVLg[8xEZ"wO\HTNDhYHH4SO)w<Ix$)hwJLw8r 9p)p"F|cOV"8PVF: 0}(4*KJAAҚ1F7̸pmMPeR-+,<LNac$f 3!uY) c0pu(6! `o5tt++rSMFN6bd#3'D``J,G7BXoOZQ0i*)ˇID+dvA`+(R˨1w0!<03'm/e2@Gx \ LɅ) ׬xq{$H*ey]{q=:qbc/e&y}V21e`IVY~{|C"Gᾘf шM)Љ즤P'$q6`OfF 73; ŠX=1A~^Sⶏͼ.\ͨp(/\S@Иy2Tc Ru40g‚UÓ;XV|N`/5]VI(r|L3ONV\ZKgcY9A2qƚgz1R-eܛ8yX]zjf)s^kJq%U!F RЁɔΓH!;~D{ 㿹klé?mNcW턋/ JY ܽ m ,15j0KC?g~Mb-CxKIEi`,F]"ɲDO]-nkݮtTcv4r8Cɳ1lisq0P3)ݴ 6K @8#j+d4GcS߷bc s[Hi^ |n6}PƀRb4JiP_Tq+P@u;Q0!TP"%R QbU!,fz?@*Ͳpb#1 #$7lӮJ>gSSߣt >2WdhT .eMVԕFЯ. Sz%_0i5LY=PL0T1=/c0Pn;' jZFdQRņ02$9Oᙝ)}~#(7,8*  Ek!.L@3 jt64&mʲAyf::UƅKh,CfwZ~˪_qo04=Ӛ$Jd@2[({g[%K>oXLypDNM#!wh!=vbb4[= @1[CaY~lDB"Ca@2F@ǃPaWPw0!ex(̑F6+Y o}_ȘDLum#f3}7!ԠҾ$#١$wgtKy8α~uuo닱i( 0L_*oOEABiY0N|A(z^k|ƨbu3ƕC|:&EEAp'cfKԫ ƏToے1[F5 Yr R ԉ؎W!( ;DeDm,1v71!4.ށ9İ6W d !?*IB )xΡl:kِeiٜə=t*[j6XH5dl<(E[r*RΓ30@N.VoSP(ɐPm/0T.k* /E_O7^ 2zё2^,ܢ=T}rɓ8SzK? =A^O9/TY _Е\74^ JǏDS xs\DVf )X5 v znt׹O(?zQV9ۊV,"P~hi?O^G{T/~bNX&V֯է43A}u{p#fWq%5hZ-d @B;9h}gֶ+8;f@(B-Ʉ;Z9vEyJKIȳ/Z+;HJ)@(/R#;`cA<ߘ2΋  9` y5,GmBO r Qp+ym7bkJPȪe&kh@AJ_uo:pU ?q>.'g]vTTZG|L)h?"]P'3J!L_ւ:@ 4>!|EuRo;#my[ #d5@fqC#`/ *5jLb$31+8p6 S|a\""| %]SyeHc8e&ϩ`H=cҗlLcE}?p}^إƖxHRF0iQ]̂Xjc< 7c JGs\>۫|ћ j@+;ٌt3zrq5׌ʠ+}KBڒU/vhx$F8`$v F( F3gb+c<LRs@VrBaW<]F8|JzU^gm3~##]@YҞĻ?Wi-7 ~z|[}6K-ʘS$P.lTʟ4c4w yZ($~2ʠ#֨VެG(>0; ]a!@`a(@Yz<%]/|r c$Ȑ:Ģ =] 0`r*]R9ofn2b Ї|3 ᥿ r6 tzzp@0V`}d0P)FDϘooO@ _ <입"kww9WS$7n>@o)YN'r'B3ϫ}6+x3N+0QgLGc!4 wcJ#uoN vLb.#l"rxM@] 3wS&th"L ҽ}po݅q`%̵NUjGUGOgAFQ)f|v_sSb u9|& $q4z9+/KPLsU3!|B9CUs n%J5m~xx4\S.W9MQk"!i"B~=+J~6键Yu2qʟ5 m7_~_<2weD{軷 -i(h/0ofߵQ8jIK*,q˄$'_֗!o"[ZN/wgԓU0lg` ,#DrkCCAYyAcl4.#1:m'ѧլŧuKU)8!Ġ=2 H˕&՞Y̍P :,pzQP>P;r&ln#SK*@83HtmuJu ѿDYqEL>&A{4+|ծz榚7~&Q96qS?#X)Heؒ68u'y9@n/άƗ:Tq|<$vqnoi vI2g8p+ 8X)BBĮ2'|KTGzK9TTu>'|*1zť3nTrJOٷsX<|eG;O< _B`U+}7?}_\'J*#M)]Vvs^dt-A31Z͛34"8'sFX`aFb{]ߓaۿ!̀Uu .Ϫ}R.;wr O lM(s2}D@> mP [?x*fi_=2;bj(nG: 4xd93'q^ )T֭<e~2_N`5m/FgKɯ t(#īs!մ(Z])@<ƒmZ?K$G|RaL{@8wUG)"&@(h ׭25CNp{%[n3ۖWd?89\6ӭq8mK |50cL7jEkc-o~e6({6̘ `eNfbA1I!.Y6:eB- {] tر*u> ~Pu& O 4!#LJ|v 06SUJoz3~7bf`TV-0HT>~BΧs1q1u C$ Fgqj=Fcs (% ͵s\_^,'/‡/9 KPӌ7StC*yN;Y7J% SUpϚ,8{$rҊZܮڕɷ)3OZQ̸2A57AG;UƋo<&/p<8|ϧ s2mC|vy($Di /CwFy_|.751Ҏ%p}|?'\*FKǸ cXF[FL Ps eӈ5ӣ-u-Ztm!'0@P(k(o%tuImw@6V\hi$l'nH'j:q <0xa nJҟ'nܶg \GKR:Nb6 |g$lˢr f8x_nwHvZ(J[tiI|j xCT sH?&  q1,X0ˌRsBW֯IܥԕFQK(KFY[1|Ǻ3CEO#Z8ӊhވٍu*S, @ 3 poTx"x~˃e0}O&l1Y FI?԰a0c:xԏODJBCvJS(A^\E+k :?לw%V@=OYA_n) W맀('6xǁj#7F##SH#a94,T`3>d\\TFa'Ց& WsW=vo4$^ʣ^HJI2.cH!uΘ `pȼZ_K*<#0&r %ɈZ6( O+qCCW +„ōL*/otb38MՖ+`K U  jWS/#Nv. {$+}>&hqv{CYL',BܙJ,m0P*'8!9֔%Os\-c B3< g ܦ|qkU@ WY!:MVĨXu kYVO)}dV߅a%gy߅ <ߜUIF_<^'+/yupGcDO*y2dXfYn!$#pӋW- rRAg8Y: ɞ8勋,3*}nC6pޔ{Zto`^i c]VUO!N8+dڐ0m(a BYЍ.43#\U0Fq(8r4@wj*i AA EgGР3M3i#P򪰮a:??*0!\EAL*"XMN婞>xnK*N2 N~9W& P;e Q8m z|>]YZ^~&͔ ) deF<0T Rqs9d-F%l>-mdeݮ|x\}\Ie36=M> he$0?9uGdF 22=֦_/=ڇ<`9b4//L@qf/aOzblP:1.D(~W(<޷5!7ۈXtFt!18[Rg_._=r#N4?t6Z£}o&—C`X8ja7H/y@IVvN5.0Z 0K9pt|pq5mHe:qNv8%sqU(몞sRВ$$ԮT8/\a/QO^5$(ot 쉛)ᙫ3ׁA®+˨"ačJNt;Are3 >w/kϓrYGͦy;FOErLe\k[`zyɞWCx03pi7}fv\REVs]!hU bR]!ܭ=h1hx;D –]$tvLe]ŷ٘O2hp#S+~$r:p1;}#@1+3 q,V'JH> W|e/ ] CW6~ ;_*ujؑ 2$|Ż"@;Ӣ%ݤEBAg6$ 'o 9vq$-sk/)^X>DW{_CGi= Ƞr49&Ake.}b?׍ىX#r_$pרuʜϓ~݁rI?e?^ NFtd80÷LDSf ,o">KgKfP!?O* (u4$BK!pBw0/̎E'%n!nXS0 Te"-m):;9gu)D;< v: 3K!%t^x6UYEYNLI7D[ x8 ?e3zTf4sUu(ap.<8O*o;=._<Z^GA >kdEeq_p6wBjFZK'e%&8aC>u9]wBu0'^N:./ 9Oܛx3tVr'>֊iT4|gt4SОkjEȈddJ *Gwwڼg%L71N)sˋn<`߼q, ,(dHJc<%@ObVޚTU )b+gF}%{cy@L.[p(PΛs y#~q*\8Z@ CfW}yt4ԨOI'jS9>rկx5 ysd9 ڍtٜ'MAWQkHsrР^WӍS_]vZGuWv0W̧ ⑜BA+KdSV|J>EB]WDqxg ODb|uf_pOR$G᳎92#Gh/rNO%|3Z"swx w d<@k+%fkF)+@([5lrD=e!'#­7X"f*XaVޖ( FfDX/ QcϊIysw@L7vu1!FcD+L;v0|E1wr+!GyX'rbD#+t!*AaShY@tّHR gd;t;?*cÄ?vaopvuVp5yMb9äx%ϯd@R}R';]ej[W yВnz\9ܼ`(i׸{TIhg^_k[##<${CC~O?MG$-xzԧN4_r Gqvu`\Ǒؑ\C?|ѯ#nxion[clbř 3 (ZJWưR0cc'`W*e۾-TJŢ峾[GVER̾)&hd Q=J_](s(̥zؠQiMԪg8LsߵP>}όA"]׮oIïa33|? Uq-gc]:@U96r*'CIV6ɼΝS9ƄS]CWOGݏ<%\ a n ^trW 8E4wHqʀC+o' \Ҝo1y%l2zVY%+e6lq)x3;?wߍ =ɔI~x3~Og9ɯ6Wck+Qe\] ƸܔظyL=|Ip{mp'98b 6³酉 e:*R6m3)~q ie,*7lć>s*3R0SSV6>}Owr"٤ 3<gQGCȣj,e@C0pxi@'@܎y^ĥOj+*/M7,V|_5VNhOFSX`w#,kMBVqح܅')ѤPC1piC؆7S? q^VbSCtq s "Q. p7q x(o[R_ةQOId,4OmDF۴:E y}c`q~%/kTzF~Uv GxB` >Γ$z&]`r=w:w+xqKc2}WsP|:ts\w~8k\ *fՕ4USHR.ܙ'Zvsiix$NS۞\#}[<8a`#6s>F4ۭ|ˏ@+VeM.qq8{ HNN<_O]? yCJaGPx"a9&kP妼Q)b ^R=! _7=oSp8׊etcwQ@?wT&fq +w,``&xm션hndS̉G8^Q|PB4, -%'a<|& Ƌ_@ْ‘VuoetdgU_]WڲH WgŎSiT@ \+P3@}w=KX]f~IǹO>4T}VTs!T&SRfGY3.u(R\Ie J.es,5;Ÿ7y jM Kf4$kFG+_8Y. gLt7bbjZʿ%r^(Uo%V4ݼj_,ē$<" c!<!<S|Z1X/cJڬd<|yӼYQp6$\xȫy0O5]-BS2%#R:52dň4$_$\^ {0.f*|$9m R1u؁'R*K}W/w5wޥp`9"|wpcQgq)q*]ݍ Ǯ1<K}JN)t~PiUMWT\|tpE#2[.m'T]+\/&xhۍ; _eҗl\g7qQa6.OsO=Ss{<>ok.qXuϏ䘐+qݿ95o!\/fhZ~-^nYAV&FͶNUv*}DˢUF4eXM5~,f)hذ?,p)" 5)z3+غ \>[8P &E3tFmJ ̫O?ڊoىxפּ̛̚h:ɽ-pEɗR@HTx xWF?4tWydr><ty9ٸU:Е2qKm. KQwup@]|VgTv8%Ǭvs#J%ϧ< KpLtiSDl<+aA$ݦvejp-(G:2*_d22CZu4ߩ֍#!Ģ#4XP / X9w*~"_7;#71Ja\ҏ,oq*(H~ʎ2C h^!<;CO?~7 wګ&=@,d5+پa$(|Vc93byfƢ>*2DGJonUݕ@M~)=##HPeG5f#1"g8Wύ\"';?|6?x"ODZɗ] 6$*C];,m4dq )C&GIhK \-[$uʹO I:~qO՝j^mA_`><Ƽ%{O$fa+(ҘFҬwwd1$8|Tq޸]9¥n?N 36ǥ3+7ۋ+32Б- n#g}}`xqUb +O8cfnNJ][RUuʾҗ€1 le{Zv!\Xoq!Y%i"F|ECb%dEJ;]C:Qlgm ILw:U>_J<5S'7)׸(j/q㠬x eUnR| mԧ0yx{[pP[(}uF`FbLe&Gcq^H{$~kdtp"nJ { gOO ThPf%_rpYbZi¬ϹzU yMMI'|7Y=w}qyr65qr?zqru4&vcQy vs71Wו?M<.Volok165)kښm!77ֵ}n&xY?C|2Z?~<_6bWh3s?]bŒ!F&7R%D +5wM ^LΌX\W?21M\.V?;ty8JvBeLRlYWF_l#sqcOɍCō|^|3>pFTGksDGP'F}r)Ht+{;:NuƸJ}_r}#!=9Pus V *Jz08rφ;wa0w@71i< %A-'iL>mn' z;@NT,ӖP|Q/&u%߀:_^?~-#'?_ 2 9hA69׹ȫ˵ q*(̢aM2`+_𳹱Gxyi)6[S3k++櫀eg,r pp"ƀ\_~ fnÿ1>>ff S,Þ9f6+0 K"Q0APn#SGa|)ÚCS>:m[|*jTS;l鑼r*&)W|KAOa>dɤaBcfG+<_ [8z>fwoe)?6"!c `[V*Kg4>ӬӸZ8`*ULI=<_!$@8+4d)G^GuU!*zLZ$i ;z]eSaMwh+>9tOt_h鴥 O!7`\ tK;<AigO_^o?@ưڿ- .kHvpӲ (M~U /"r_}ʒo4b|b"''DĆV /Ɓ1;|  4 ױgR]mJ˷@-F;e"81 Z[(^䊏-7*5S1.c_S;Nj)Gy^vQ+ftP-S жo\jE +67 F;~ Qŕ2&FSf*Ս, SO(⣄A1VqWiC!;4!3#qfx-jTf'WE#׉A퀼.H<k!./]%UU$g"=Yy+-ŧRJ:fipQ.tOm&MJ6U?mCGP*{Q>)]GPQBd+~L@dkѕQɩqɞD+I+:+4oS36HcM+w=t33`,23Mc|M} Pg>܌/{3 w J%z|31+zl̹A+&G!KUi$ aBS 2Ձ1 ]H-vY1J|3g+A9bfeƢ7˧4S{ıXZW %TGf{KK?7 s 9WiJe66JZڔ+K9%CxDp0"=% 5S"ʆtqO%+vvUz\#Ͼjq:HڈEE!_np9B[rHYّAsC-GxT8:A01)K_ٹ9osݿ!8Yqf{&BZ?^& 8wq#~>STG 9v}RPPV0%e 8Wi0@K]Է8:=̞=c:ZFC~;4W\"_F aHte$lq]Oi~g>y`77>sS|$aQ9q/ @1}\ c'O_'#}?w^X7J]4l,_åOׁ6覚3ag$^PO+ ÒɳrD0!;мw@H6Ri3sa[z 5ݿ;#Z!MWZaV~? ƼoBv G9JE{S'gbzv.(eo#01Ə̰g %NionmQ9s/;|ov%,fW`jESbB 't-OJհ&Uh_:<X`C<W+:٪&3@9VOOy2M:U/Mna@w5L1n)HIx_0*ce"lmʼn3#k׈cȊZ@*R@/q pBD5j_緞n{†ͨ?Qv÷RV_uZ1+~m.G0jEI}m+ܾO3/o4bы#G{lČdE:wѠ?0}brA{w#2մ bױђ8(zcw7j}c}TVԔ6%#>{brnyCx>~8&Yj(BliDgB2,1I@43BGt#*3ٟ DhRZ_T:sJ.xtfeas9B; FyՉ`~n2TWPFv01.1brt%r=Cǐapcm+>?u*[KSsͬG(^!X'q ؒ * k5|~Ig)43iRtp-)K\!:;}+‹~d2/G IWǧE_؊=RǸ+=ƿ ~͛@0UKl~<Z-ɘ6nd#ȈI@10ZT\WxDwsEV̿FmN7 y990,R W>>od˞'zl5AB^Q{l1_ޜkXM(ŌIC2 a!g4^o?oG>rJ P 6,I5 as+)F4;1+wAKJS7F'1Sa7 /I5tw3t~gOFS3^r::|e>o׽e1;ˏNJȆ @!5J>Qi |qIC&5P|0)5O6@#ӡLSٚ<ВG,"wM>(fhJo4?jx(K3J:7CRgύ{?QX?("73M|ғ' ݦ?-ʀFG/v )^^d+vJ{;Axs|D/Bsb_BddWBF.u/-noWi1:o{.&dTmDKb6=| O.(iI+`=8Fѡ?#.Giz>]H,%QiI!-}g7/m&푖0/.h|qӫ@vo^g?o_2[{laDb:cm0n D*ǯ¥V9R!zIN߹Děb\c0t/ ghtFw%*ĜP:{Ņ02 /q/a|_8Ve 0>߱?n9s,)V==Y?U[(L W? !< CN8'_K4ح ӈ';_UF%̟|zWDqSstsx;eZSd~Di6?ݎP/7G1Cy_M|>1/^]ȕ2 #h~ m׏olɑeW@3Z[c~m}7-X Ɯ6a>/rz6/ioߎ1;5[ma.HG`9:\#gk',xg 'Ed%qb&~G]sc^-{p=€FO-P⍧es?/eIտJ&&_%}Z>s4 t#u(a_Ƈ0 ,td N*2\~o_îQ>N?8wd?1Kơ/'ՎO؊id8f1A] }\*)3_4Ǥ; }BPxu05o^WM? }9SyUi\I*7|Y,C6OĄ)⃈@y2bBnj+ފ~EŞMq_* cƄA%(& <Ǟ^gdXh]+sY^V?}=c2:Z6RZyOꟕꀩ=b|vx_~iZ 5{ 1. ruR; )q4i fz(K榔t!OM(Y&؊0bNeՍuط_q$=13qJ`8d[ agzKYAυ{1;yw5dK'kO+)++g:cZ*W$ eEWN"mHa+SʑE8y.G?W~O`;:ߗdѓ*y bq'QG""&'GDiA㓒U/DEa%x/"7;pYcc|`}w~ׯƁiYLB].`hR @xgl,k`ՊK ]UĞ|JE 3i|,rS%]e||Vu(љ3D^>ҧ5/E4QVcwʈ|+IJָ0R2n 0T'@0skvFy!<*%`AazPGUmxP~+ À;mG-UF2G͏x 4~ ,ew65.D|ŗ?Cg%xo(K ~銠PI]K|>g҇Ks|aK?R8kW~?Zcév9.il* JQPQQN5n+^Q&˔4bz)A%qJ'7$hJjE5=~c~#1)޲̡QACnc{Uڅ-ԯQ! w\-xiqe_ˏ6H4g%>] V0ٹ#ۛwp_N}"#t7SC5NY^w+"Gqp@ɕwz;pg|L+K A(wO)L ʞ萼Ed?務0Bz|@]6 ^ӕOK^fsBRN7rF[|`F]g PF}P ]a#Ȗ|6ApA{0hP:ꏿ((!0(aW=DŽlwW.B;c!G{:O+0St zP/>[mM;n~o;s.&t͍en>0 %y.iGa܍w (yu ahFu\K5 w%et%l+yNW:%-NwЧ!O6ut8HL⺐Q%8xԕNP?9>ڋ͑lƏh#kN2Adfd $q[U+z;փ)yqJfUcq 07)GYʝیGbkSYQn@]{ʲ3/CF``\m7CxPp|+[_xȯ0(_J6{0&aܒೠ*2jIE+p]^}t ȭ(F흉RWRQJ:7uxg@)w2_o9}6%DGIrYamM_ixFϷap) Zz:jL'PGg.'a;@5_% @?`UуNx "'wGZgN*NȘ < U~\L)?2=_AfVTA;_ ͰڂAAuRГ^rZi?;~oڇ@ZZs# |9l j[ѻ^zHc 7 7?6oAASd4m:G#PiOaV~0I: S Iq@Q; գ6g·/þ75{db^ZɄAD` vT<@z$~^ڕ#P r_g y!(LdԱl䖶~i׹%![yFN'tj9Q|u>C7`EsSԄxKSy!?4 _id2 ,PoDK8?kݑLbG_oe7](jK| ?'jwCi oXeZm.?> xoBL#-&0 vil۱eNPR@b6arʃ*Fr ŊBR"2wQ'V҉2ѹF?|flF֌T#<?* ..21BU/1_)8 Muj,^i\OE{ވkm̡΍W,RY~:0/AhZCYT@51>Oʠ vZ[[{f1⭯\C|Mqp)F*? d퐼]T" 8P^X" 5kG4 3vfe;EZisRpj:GefW皃)_'@ ".q|ODdgϣmO>=o~ݯF= 8$ D$L(1hIKqdeY^ֲ8;IHVql%xIIA)ř   uNg| @y:i׮]v]U)8\7_0G3#{?@HhwK3ܷF杴fm~ws/ou9^&(|0_hمȗy2k'ە_A}]Knle<{I`u7?W/_>>;_i'4 F*ܕ#&+~3/蓀櫁}ܧf0q[RW~&alwP2b-Va5/_Ҩ8RG;p˂zV#yAmw^/|lanʣ;~0}7,F܉&< B-0>50V_wUgڷ2džv(ȓME 'l>xKݩ<&&s{:wo0H}]? ok}M~&W(jϻlSR[|̶OwNW59at#BpqDpkݫb b++}[zd:inCpѷa3W-kX:F̈ nF*uKu` Ģy }Lps;L$N27Qo>cj#=c0. d/0tk-8ִ:\g z]O[h~tH0 Hޒ7*rl:!m1bȣ>GࡈC-q IQ{m3g3^rnۛ;3U^qo@v4+aZa`Ҽ #̀nww?`ypMO\hnyG;1Q'? ;y'/jp6O8Q]jhLX5A X;||7Oo̠;|50:<ΉBHRtH`^|M^PΛ崬 I%nqks2ȳLclUT o&תIr;_sD~s zZ5&1H~̴So}ǃlN U _~ʼn?唤J3n#S0hc{|"j,KKR*q׉EzpU~?UqIŎw_B.OyZaSk\t5ꕦl'ol~18o_'K?^߹ҾyO\+_ɇx{CZpw~Хݙ 7x\.8߱DAV1+qq]GͳFyI?DR^XxМI82lE.JCuBчO}|}1Vȹ81"aAX)T8&aD~F$BkC:{x9,6o&}yoq3#NTCxE6E@X?̳s/ 䅷t92x\ ЁQ "} []v9̅@(3wχz'yJYwh۷8k{C擻Qwv$2JH#lc<͂N^8h~ @‰KToz7ʦס#̬- _"/`;=i db"RUPb.L,)nB9NB'(=L0^)R Iu-m=o>D5~NߪMA]*"rǑũkj ,"#ZHHi\Jց!aǜ{F}#=jo_D^tS+`u(o^c<{g}r6FCl!AoxvnPݘ7]VtQl$+̦Nz383% „o`&;cg]>ZZѷW/htɳ7׾W{M+I+X ְ%Ws_#4p̀kv]I))Z<PTQcU 4LS!w-D(5uphuc&Է1X`pt}֮ۆ^mmyV&`KEkɉUiX?>: LC_'~ ؇nXޕC}b )Jh& A`C9WJ ' AHC|GR)1@T$a}@v͠ nz϶t{'6/|VeO][.|!옗Q `#*lGeUiB 僻KorӋ\`GF/t9U9<)))D^.X s$E?=H˔G] " v|v֞h}V;wt.3.6&L'֗1cA0ivCb\ʼ^qIݧM[P*]l!V4AsW;=kLTPpl9Kgw%9JA$ײMPF%dzxy7k;¨Q:{6ycxng tQ-0re޶ ]OyV ^@x#7  νl){]{)b1Pq[ZJJ QTlG@$&DMrR B_x9eRFt}R4#EO0o畦A7:qןioy݉v-[ 0'ZWXQĭxKC # G4/Gthf=_nJ{"'EUK g.OӪ̠9WBbV}h:e(a*gWY#O.H<ϭB<CܔOe{jU cgBrB Oy_2^֒\U B:s*kPN8|^w?2m_״{v2Iˍ~:m6ʤ˔-:}muov{{ΫjGvz72Tܫ|/]]_Θ f.N8|M|epml_hg/=Z)g$UARCĺTZ:\>yA01m= MH-8RVca gLbNO@mƳܴ=]o Ơ)+ߠՉkW:ׄ}v2 &Cvrobz{ܐicgWzW(}[H=ǘD󁪚%fk voaCol_w33gD@n z2f^$SOd*RJ|>k'v^uWֿjO0;3x08; $-2ibWN]n}E@@=&+֋ |ƨ.J'SQ(H9Gx789~y'r-)S 1g`hL7.OLr`>"$ G)/l0'''M'$(. 7G I^4ջm/Яv?*$ uRֶh_Q7 20to}_2ʳ;c.GJ9e⹼GXMHaNc`<g\C^a+!CK)y c- ު¢DRda=14_v^ԚW<0bk^@=+j?kklֶB-Lv5&IJ4[T~$IU|78w_;y~GzUٺxc&CV*65@|  ;)9/߱c;T=JHȠ|vmK=t9!˝€gE̷r+ejGe+@cmW{uX#Ö˛ij;S4bfZ][|D]P nDpztblbЉ^N!<ʐm^U6?EvfלhodvI;Rfb0XO WZVcn^B̿O~4*tt̙h+b1wT(/!MYG6I} ;{ >o_B>׾rq\dqCu|ʼnHʪHM+++3 +ڎBfG "$HYǀ㕲& Q~lekKpȀΊS|O's'zA{9,`m{}[[?8ɍp|ُ~nsϡ+uǷDS2&k*pQu{k&-^Q/۞|n֞>ʤ8Atۗ+6;D*~r!#i ?.3ᡍNRe|K^yA9ɵL IpGHWw[?貽 r+p΃t!'q3u[=L(. w{mmԙ^U?Ľ&{_i?ʎ K&W ? C#{b%Hd{kzGWxo/mtyS+ C(ckΤ{8}x!O9Xe!1^̈́h8_3JҗeUqy*/N&(#wKWV+o} aqqV`X/]:; |6}Vʰ5INFn|Nl SVcENQD!A, O6;a|@[&;猧(tz;J7䤏ړԥO)eGz^ߞH?VX/c_fef?UFNl[M-gm]n[no^olOMY[ۓ?8NU=ډ*8Q/~#}(iYU~6{vjk/\?hO=lO>Оk/^m\ZZ}=Wz52dgDVZڔNdѠU?_;qI>aӌK{IoWD%|OWPdcWvgrPPv 5.K?x.H.HNfΟ>l埃uZLyʛcw% q҆2e6`Yg g&o*h tO8s~;:!5XO` UaJ;DkG@i w&>~:5Ui+*o8qe|;(tY)96)'1 Z2 {y̏t2EejM 㛉00ć2 Ҹ:;f/> F]"{G Oxy/D) ENNt~ 0N57Yeb4:а~|O! Gs"UyLXpzmnhEEG*7vHd?QCh"kU_^ dȰQ&UtC!"]*c8-04[pȉm B=\#1S:r|9\r7:Zog'کvz;wjcyZ;oXH8i?z3ߋb){'\Gv0T+v0tWwkCe]ezmvg|зnU+ n[ P> y(ȯr".+W#:۶fRˤUzoxBOh/c!ytV6(N/fqw| 1H2 2տ:Mhm o[tG ^iڿeo]o u}ǪF@OxN:72#Dt?vwۛVx{RsrNFзUs9 tFFbpr=qAiY۶QG.Z H?9̬;eGd a8b2$\ @ug rbs9tδ753: +IJ\FU)2F@C#^]RS>; lÃ,/bJ`OcKv;rƭ!^֮-mǰ{ǰ#CVBM?+g;Ç*8DA4x@F>wQ (JY~ޓ\1yPjS&pF)aeJ[.)̩|ۦKMg&l ʋ:j@ӯxQy Mߑ$hQ Tu!K@p yp\M)giyD,$n^ݠ\bϱjkdzh_2R0XNp(" ;.FBǸ$"8/ ȓS&XC?-Zӂ۸hG'a|xmUJZn诞F?/2\xvߨ$\7GZ;fnh;l{Zw?pخg>=yNH1pɡSEÅ:W7gxDH(]SwWd_^}s5R^!.wG* BYC(V p0#;/_cVy*7=|h nɻ@"e ^!FW0 `uh\϶ҳ ._(//X=I-4x>=!0(Y6̳̮B4xc_,l[sca(yQ:)yаvI KdXa`zbNvgAJo;os9tcz9IT?qEqzd|%9R& jpR`I R_(hSU8taL?#Wz d`8Q^t9ӒC9ȡXL;*4 Wv4q05R T/?5Pd9xN^P-ˆzG|V=4qUX0 $F8P۬qev u?$poޥ5VtbQ+->w|BwuON84I",]Ƞ Cg`+K&ÕʡsK#x.&V#21'ʟs¹$ PpJ&oğxϲv;~b9⑧%9YT*6LL~[t?; t>=gv`kt!z/~/w3UbHb\Z"0^o iX)l6X2}anQ vpb&m}^|0q@J(xu^%Lf>Pk2z| sU~0&ZKW5 x |Šy Q| mu'ZWB.*l3n i$ ozcTvH{['R/ ba"/'rDbf>4 @D!֜Qn*bԀ۾r)oaq90d g qISKiy9~yUU  )muv1uB&nț<cf6Nhr1e X"|rʾyeK11,MV*LGOzězk Wj֍#㾀s)+aXFA;t6 e+V@eL{ncc"*;V  MO^^ . pv(/3;=loaQwv\V[K68<ɜbs$OL) `|'|'-xT}q6U{_~]2r/GloG꺣{^9D~v rjV,9%JS3*HGz5.r~R *+,#Lqk~:׼91HZ`湮HJ&a <~"g%=Tj â~Gø8SO]`U/ b3ys&PX UY0 '(g%D4&C'nĈc׼\@]҅/>P6*CMM *nMc)J4i%;ĈZu>4>72zܕ-Ccp[Hcr/Or@2)(ӽ8hZ Ɨ `c92Z;.cXpQk od9O~mg _h5@A"eAGI(o>-I5Aa Xbp-: *_\^$%AC簨 n~ufeRϾwҞ~NNam'Zeܝ)>0}ljƄH89DC}&`iۿ~}?ҞoCNgwb\=B5e /i4hPᮕ0 'EU!h>aBL7d릶sL0s߁I}XU=j|zrz1;{#o+ƱI"LJVb/Q_v}C> 3P9rN4C&u kFYoF oy)Pa蓖k>9p1mC ;u"201"[uY f|($\dlJȉ$K'W=<]Y%62/08bAraS .j4R~LL*^yE|{p/CCOA:~LLsˣWC[ҢL1ԕ#- d{t6+C_3yHE~P}6| k'cy'̓= s޶I9&/wN{A\&<륽[SOûz¦׸cWy9\$a:H^vX[Xq0n  =&L/d۹,]qʻAC"HId TlylAjábgqIS.F i̞w#>21eD' wžOl8@$?qGdşU 5 TҦ;$,>V~.+\>W¬ '4DQ΄pi#=yz/V ՛ʠMci@0BnYm_U)z58ˡXp/κ mym}|\bQik(awR4">/1=:ְ_d+/=U3% >H]&L@3<`*;Ogb m14KzBH-Ir{64_RB2FJ&v`p''M8B$Vݦi^Lt ȧ2DeWI5|p;E$,uRHo&ʓ0В.(nyZ‘pgEbٕpBg1(Qs8]{nM^y^ 6b.#4 hd복}s 8}1iSOwƣp˻0npwO=tl$`$!~uǡD#+~@+Kɇb?7SW?WXFZ# X+Ns0(+$ރ]EH!jm lk1zR:2IZHu d; 4UAV{hpv 0~6tbrŜІʠD@eC"ruJd.F^ۗ Uxv<C5Γ \Bó5ƼÅ+o;UKno iX4J{b- Z44&S U <ۢ\{I8fmKҗ?NmlB VRU+渂;P:Ci;8Jy?&o(^^l@p_0 BP<{>IG ӾGS rR["Ϻ [OYy$8!M!%4gq,&dc\gL@CL-zx&pp m}m;(LP CDMc`r'_PW$`2U )>QC!ho7&TH7 e v6%0#ʣF8$3mpHˀĽQ%+wֱMʋ$Ā;a,X\IAg@]D!GS#<n}N%HULX)Hmf_:CvR'l GdSe%&mS5`4PZT^w2aeĒ(#O$L0[#mT_/:l2d΂X[QUFLL9ia@sohxS_Xq*NpiΘJ+-_o\xߋa@hFZot MOم\7e}+ o_yelO51}yW01x WeU .S+ߓ^E_Xy9gە: p ퟶ\O[lg`w1/wɓ_[ed%- VWrGgօ:82p d0 #@B=Wy|T*,ڗw3hԭo[H\|_ً! PkŔf&ɂߏ -<|綥- 4SAc7Y}E0"3s0|6'9y}1tz?FZ%1o%^AL'87w\!SwdӺ]lD$$ƥϔ3 Jʞ_;hgN0D XآqW\꿎{^4c"wJiqݓ/HU\+Fۊ'L>sm:odlD:a|}of:m!PNWVF|H'$$B41A o8&,[{K_\oOwmowY;()T %< y願 ܀Fqh2KXn҉2`P^JkӲ}Jiw``"suo#z*"4'wu-׻5%Х=޽" QľAE>(6?J΀ybHYI H; <-3 d6_ V1@vuQ 7i ⁔$7n$PS=X:04P7@x˅)cӍŔ t !b]:4V6Fr?EC\:ՈnpZ@€.͕ ÿI%kUOP0\y ZVG^d^D]J+e"sG$ ĭcGN"\eȶwW m6']w?ye [']CxvM7\y/6LT%9NsZ@5'{f {2lo86_!die_}Wbvb#㲔4J2q-J@ 1*EE4ٍw @TWiYao\`yWʭ{pN/1a% |.oG0E+᧸P '*UҘ; rR!hȷlgu7==W׿r%h/ *U{ݳ 02N58҃,-C@N$"++SD&mDB4Bq ?GFK61Н-hBBgq]OՓ\ Sh1M|ywWPt\j.ޞc*o^GG/rg,u"yWu+6Oވ9 40v-F|O(YxY{ "9W_ܬb2w$,h9 )^JWV1Lj51ʅh7lLfԩRi#Ȕxm@Eq}OlwE.Zת\n]ᘵsx>xG?վ_n'b2RUkg^][rAݕJ$Y6><ڦƁM<Ku68X@_UB[nӷ=Ԗ̖{|K@7( nfO.yv'1*!p*4 }yßiyCAmqo~j՗m\ϤG䗏8f'Pt>VA>zM;h VODiTCmb=h[jaƧL%*L(EJ;gR?E>@B. NXrOD4LJ[L^#ry˒ @&=E_M1!5._?FQ-o![/y Sd$-ᔓLXXNu\?v}8@Re7.]mk֫݀bzǩ&v4_*4Б,jRg5>>eG8etrk" o'7~o{_iO<[mɈW]?:l~|qʂ 5^N! k mreVwGQ 14%#i[ Fή1Ż;֩ib@@KEᚮP&P-K8 )| z&JSʢxܒ:\c$H#!QMwI 淢ΓN Tr=I]뤔_pe<L^ZA5Uxl&$6SX s=ֲjI 6mKM%/^nHd>!/e`Vr/iNȯx7QB94aN2x9b+7=ml &5r#A;&q nxFe Og& L888c nj M6H N{ˌīQMV+#ʟ8ezlΥ,|(d7 @ݏtUJ'$d.z)d6Z/(7E'yʌ|N7rR)Ip+D'UI/-_b7*:w!/A*V~ ,%޸|}ϼ|*dHeo24L4Iɾȏ=OﺯM7ܽqpG~ڳ}md寀j'kOگ>JکE-.(bH8(lc($ 30"m`tjP D[_qc0$i]WRӞAw'ZXF)göZ*Q٩uJ ++z/ ޲ՖچLH4L|nSn%lE;]oJj $-w6- w>?7m󻁁| K堘_ZYnio^r6<^='ByMgL>Zhnu'NL& ?|­{7}>7j4if#1!㒎`By7:-IdwF1w7am.ź:q~}nz*,:Ic%Fs ?;->|[e}1BCM1.?fǁ#[1Iҕ\b8'on({&b _MۥG^ HK}ioOCFF^HgKxbiwˠE6m<֕}+LAKY/qW\BunAFda i7$ξbg祋Shj?ٓmz ƻʲmVovL0Xi907YBn0%me,A/=2a#wp  TY F"͵ӛ΃l%JK|Io&tDExv[8Nsn@𫋵];@f < 贈Cx2U2f?Bv>P`rye!b#c2A_p&jL`wdCBCm9/]2ؾLz+]#!߶{/Q@%Ƕ:! (ÒiX:#NJҮ:l c4 }Xķ{K'Bb'֩wh8}ɽv T907Rܤe'~^9hO>%1:4U3-*ymep:HsT'vX3?jAh3\ 8q!ʩSnvm'qāI&_@3PkfmCͬf rf|Z-g%M6vخaQ@PiSSe֛16Yi]Ai=?0 Ax-P nUmKHBIL|Y ݛ9MZg-,&C|[jYs)eȆž`MLZh}\R SRg+7כ)k^1K0)FuYr`)i,[q/-dG>ʫu:vݾ' D>rԺth#ț'Q1!'A-mNy\GgMW^"vYVpyɉ_&4O>x@#w!_j/7N!'8wم`clQrr*1I @c}z}ﲽfA*.@dM*S ?mqwkV,U|~~8'Lu.?¡6wE1.?JBiz>*Q%N9~ڛO~'޵@|&'Dg?ަƫN#twO>lh^FW8W7q8smPr4M(Np3 ,NlB*-EAo`+$$;Sva̲g$.8SCPh&*99(c4kv'xv ^Ky(Cifvs}|/}:!塄 j8젆)'aԇˊr]*lٺvC#T 'Lb4nM0`51DIĴ)o%<4ibqzA+V@N>Xnh q5l9Ҳ:S0]v_t j@c\i!,MQ|Ȋ>*1e])Je8uex81 #-oH 1(r,S,޾Jz.pl~xqSM>'&jzw ڞ4 S~q[?^O?>;2x:0㚏(}f27H\ bرPHt1+Rt}[ Y88FؾzP < 4FG;mw,ڳ[ӓ FOp/6:Y4fA}#WThS嚪+ Шe6&+eyK֋r ^;Gu59w_^R30JW̨d^=D$~up'J9ċ"M*V]*0 %q˵tF" (,rLX̕+ w*PّdItp;J?uN("!X4,Hj@Z~JqVve8hAA<9>!HA8 @@n")CZ|n˙'*_1/X'*8 :md10ۄ*,k`w.tsaը|X?4&JeYJ2 0)?gu<\-&vC~8ѷFȨt !*ylt$i gekL?2o'Pa%~0*loj;-ldF*r\ԓyFSzKͲ>ۗ`06Б#3"N2P|0=ڝ-E^ne98i?@Nxif`WG޶~c+(Q^( XS>GMRnڹkmC{`>+yT^s(i3("hqJgn T4* 6=0!J(aDRY%e>4 +yY:1!ʬb!e'[՚d{~~>!޷mh 2ҟ^= KgGgY:'+R$idkw"(:I^f_kK1, U%C> GZi+ԓ"SLbB Qwѝ~#9 0p«~DŰfKi|WcAߝ os<(@uG$>>=I)}vv%~6y!Cǐ>*7\-׽%3Qp[ m yF 2q.4G(C9kאj8u7}Iq5d'Fߝ.oU%xoKeH!a0g ˮ;^ivgJ[' B ?h`}h+J=AG^~g =4prҮ\!ώq^px\zU0 pۮ\|z^O܏9BWgG fܼD@2xxT3м>νJE;z(OD\CofWXVr˾Ӭmw 2Yq&= L\ q+uî[6 ś T,S.`:NvyF^; p%꫅c-$/y)j BB^(e(m w mXbRk'U^:ם]#3֛2CIEڎ1zR18m>PXS<;Kmr&Rn ,36kLC̗Luhݯnp }c|Y0xjݻC7ٽ5@ lSv0DJ9BԄ:lg -h3?!91u!clІN4>飛?K2A_ /bN%G'rQ` w }"WɌ !?#1r W|'GV!+߼U ;IB;%F: Oy-f>a|rm\qq [Rt[b*$^ #G뗟ǟ(D/|]Ycj7eGa#H|'#v.+6薂KV:#DLVs3;ŊE uۦ[ t,|!;(Pڻ3r{r@m(auETH?ɋA5cH+LYs'z):کr7џ74G}=pcus#(\vZ|!-<Η*9I}WWaAz#Z>L<X_jĽ>qe=mq,1L(>iAƲ͉ʓFeBA{#U;_[CTvzSbd40yihZz_U꼯EДQo>N]2] K~rQ].<)Xoc~qŠs[yN.dPDh6mvbۭGKUI%e5n&dWɾUf#; NHs%oUg&vN`hμ&U@/xY_ɟKu9 ]͌\Ƒ^R~l9lO/b?8]m2aOApXYpEvCz8}$n(|ö${Tܪt&w&FɬOӎ.*Z0 xE͵2+G s%ݗ y3-}C𞴗{YM>ا~}; %DO7Qr^˽XxfѾpA3:5ʭ "H@8g#Âe/hMWںL@Ղ+?2DyNCj[y9`w=>򉭢IZC K`H?bdFbh‰É4W9AEQxaVڪ*# 9s8]ල Qx 8}Y* Rn;_ O`OQ[?īSU'M?XNttox떽+0P6*7~mk#U_&ʅ8A&+iA=J0UʹS'cHABPmM$S,&H՛)7aj-dXM[ܓa$1Wwezpwx`g|Qov%|t`- l.}i;GAwT'k=tn^."Kqr 3&}UUWF|Pv m|ySP9,^G-r֛| oO& ASpTNԄ{Y>`cLǖ)#H [ /2wl](s%Vq~I;C^ˀ3s$@}Fs2,! }Т'p n+iGڮbD'4H*/t[N[+*d|I@i= {Wbgi o/|_nc7^`8FPy]0>tRδA8CsPzo&*|W|+ |ن_aXW&xTtvA~h$="o)1Q>]Y^cƳe.=Ї[FJѹ0ܬHI\ބg94tXx|Wu G/iU ]N@ŗt`+d_;R0pe Nylk{Bu:k\#.O)K_΍OU^>1GiE"ڑDEXn1+oZԇ nwd,cYxkZ3oea"K]"2I+2{8H*[ _!RV0eT1=[Oఆ8+ n8iCJӉZzg{mXsnl<_o/>0 "8;70Y Awaa;]¦kG QV:ƪ tɀMz-VQ,(>֞hHUpļT-hy6 ?@'ݦU83O-`SYh܂j N-/qhCVS*_ F畧W o?mJu^^v%j(`yJàhd4*~ 4A|r@e$UoFG0NY% "N~(KB%!y/ilSq5IYFcb#\&b}IRIr{Wg$țKy'_0trPO^3lOWGqUh Ur+cWP d3YzmB=c ɋli@d9v<>LD8/B޼WgXHS/ͣ||x3ab4v(c_ЖL3!e_홰'Gi>jOG\~voyY|=e1t{ !8 !ɣ>xO}t.8e3*țT( Bp7΁W* 5z/=-<|Ҿ5ГVO; }vO4e f`uVҥ5Ԡ\"\qa~9#C+Z.Bsn(l dr ;442r'{h ]8|ɉb;h\!Q?TsBBz U.߹INF4,i:~ !bmIWC&.11+Itͪ^8OU m7<X2I[1l!(H`Z ܕ\j@F,7I8䃾2VmfrDVy r9<ϖ/"*ɀ h>͍4_>D i0ŽȨs A&nH*O11ش+u%W^|ʁz7oLBh8Yp͇$E@)e]%O Ky12}wJ'4YeV<-& X gXkzӢ;Xԫ~).&%§OIǣ_e=,Bz$b~qg s* [<d=Ew)/AVNTGFDY93y١Hb0WV+է _{vko^~n:!(Aפ@妄M3_suo<>Qzy:<\80$B(F }v;ng+*ve{([FŊ`:^?ϾpLAZ"G Yܕ.&8O'@~dH[5hCǕSɋ6dQ1Z WF8_/TR d"anjb$ȓà݆j ss`@{)b ~H#=#vRAfEh\m/^ "{xʤwti   `0v F|r(|16|R> tW̎t'=Eѿx>:yl,/ݾ/?OrK/NiK vvW }”63ы]W:/mGntv}2y䫓#J4dEiov8p!iҬ!Y'pm')#o=ʯ2))<] YO7F T0-zzokK\auy/~hq( #3jzjM,ޥ e{B_Ù&;~~zXj~~;ԣ|<+<[ J_XqޗwbQkOZlHfKK*G& ^bJd$xw?Nl`e{FUi\#]Uu5tȣ o9BnƤ_Np`/N$a{z>0_9Ui_1dlƥ(oЈi y'JOe4e2LZBW3&)SO& ˨dBJ4MeL#ϛuQ1P*m^^z/Tny 9+e'AAK4j{)1̶-Ŀ@zM']\mK +T[UJNHHafqQU8RХe)_bpї,tzzK}\!lKd2ʁ+ _pWf5.nVxs⃇y{.Z6Mn/{#N.n - /\!v闩rC8U& 9rкߪpl ~'v 1֝\JRf1:0BWU+Og‰dG8T֙Uh2Z{}w~g:^BKk"$  qXe.Pg҉[vYtf "̵O|2taqZr ||ۮ!m2&Nv;rn AҥQejKrM7h }+Fs81L4~uONX$:?E8JO̴'DcE} ΉpIѓ&Zr,Ws m$~dо#쟳["5-$Q;N&&@ۖ2Zyu֦?˜F7h"nÛtȨ# %ԕN'.; 0v B )9 H,L1*-#k `{߱l}˟GݶQņ$^{GLW$oZqMOTIA?'6U~3):Π&We׮PvI&q[L_5WNMk ?ch aK7ܳLw_%1D#0xHWcTIӐŘ~KGyϯ.5 b3T Ԙ|;r1ox;_ y3,f|'k׬ūqwwy 8pJU$~ |L+Ce16kw\G>2A͗1duÕId%Er} :M.O:"Ki8HKJ77+Ȝ?tPYOIp /BaS_pQVPۣ(8Ȥ@XC'X%5 _C2S31.pF Q҆_^_΍+]$|vٟѵrWC8:-|*cnIZBc$*PoXlX{m1 FK1]IS`H.rRH0rLJ&-#+{ |&8a sV|\t馥LiU ґ:—ݫϷ|nHwCO_3vdC*k]Wڅ+pn%^4rv~z]ii %-_ .׾ܖwթ""b[1x,#J z^j:N;_ڟZFW\+zDO}]w7:k©VAǁ@l:gqt˥^]!<)'$s%Ukq5`|o^pJ . C/%| ԑz]*IZ^z$/KH>1>G_4>T_Zc8 U ;C;yBIszC!ϣo UGqu}JWj3yp0k |ymy/A@۞Lm7(O>*Fbu3)y'oe1,1ImgHjVs@gn8tH'e";=!uq;+J|dtH Y;7u&=)a]`#0ׯyt{s&6"8#?ߜ^jI,# iɑ9`Aғt– =2GY`i_{NT;G{=Oױo:<*7m@ؠ)! car*)6yF JA:s J ߊf֞[o}<êz9mTnZ `9iwZ/lO]f}[/*β~0,~h#,(p+;rCgÉásk7#Бծ0'L*`nX#=%w5xhNJ4^w(ϸiZ+$c<4*t{S[ } qo h޽_="_dPMr9&eʾinWo, .2_L]1}/x YprN$0hu$ɧaI]o!MPٳ-X*WK&vye.Q8g 7ezg;ox7ICOĶiWS rH8g sUV?q_otp%uX:˸w|#Ĕ_qHcg?)H0x/OdU~e#+ Ǝk9@"aXvpu>w oPaCHB;Q`|%C“P 7ߺǙD,L mU !p`*4/r*(( +dѽ ja yZOTKT7)C膦z&Vx eK21`iA\!QV7x'fubEg0kU(*mdH/]Pjxpcf[ I`q@.nպMX8 ]5n2}Ί"qq-uqK{;_Oץ;lR,XY?XJ<>h[FDa(H(Gz/^rR$N'0cWL{c+7-[bLE~cO*[bp&4s9i0W{u,kQXxx#I3JSK}t!)0 ` NDNkg}_ꉰ˖vZL;^:j{5DMP tԛgྦrI_z/y7o >_10ah!v%xd0O-w!aމ)g<_X=ui>}i=eSV~SZAI~(` /j9EP*e>̢wmP'vlX׉6>λް}a}i3xe哼(& WRV.W<~Dz=Ivsg"@iť ϱkgE@j"e+EہGv_9Q6KoD[ܲ/!ߗ,4"YgȨ 0rieЩGs9¸maBJM\=:t2J\Wjzsg"'O5iiήC7BYCs&W#8=csrp[_y9bVTg6?Iƭ|q\k5,h=[p{|1&8:m/w9J!;& =߃i㱏ʼn 't Iжd&ChgYw1HB@& @zti7eg,ߟy߲a36.sEceD` gZF:.|R';U"iK;DNVxˈg q|HӷlpUqIIA`ˢ7a'|w\9 (08Ba0*e}#9:zvfr֣ !NCDqCXe>'ɪG<0~alB 4#bShKI[oc>ƽ-g6 2U⁐y\֣^sX eA!Q<KH z;@2L_^')N_\0:(KT[cDjmHgbq4JA@z /xqdK'?|\dO;yB|Ҡх&s傐.We[bvƊEyX=GNri+9LTV=R(!#0<ǞwUGp#1n}(bPRZUJD( ዋߵE`!YEXuʋ xtǘkLj>vZiYdIhْ@'_ Ί.~ (q Ni[kT[u k/1@An[ Qq8R'YQkЯ;Ր-"In7[J cn^ojն7QO^袢Vk5ޠdE#0 g 4(EQJ4L|0 }37WYMR)Ƀa*ߤ:i$2}#ࢠmҮr 5#_c y80pD0V6r1C&0 VDkەm㊬>WdgtOY1VɶB߆ ?2B~RGeeJy(/Dg{ w#WF]gҧ ?{D/Hwh$z6}b/&q$K_=".nhӢG?ã*L:E10&UN$\0ҍ(鞒.i|\I~Qj0:S|Nvzɓq >:`$jjba12Plv߂E< iWy_dw%tcr0mBSɷH7W"C9ȳ[@|tX69h?ٍ,;X|%թC$PЩ|-/ ?.p4S vͬRB3ʕ`@(Ӕ5.>y7ֳrp6Nj$1ăQg[F7VjhhuO+{J6Ҍ^{E^vY.w}`Vunu܌R1]xYw{H_{}LӜ,\aWWm,r7~Lv Ә!qr!4޵z?|ݑs>ĸ?c\i #j/ \=^H iI?pj9-c%Ⱥ4V) -R>F`d?O` pL ڰ1AL%K[ 0O`s/nw#mNp&o3߾-3M1޴G /B.)t8 8>pBId| C76+}+~wSǿŮsS`h\CdI%#ڤ ))[Mqif0ómZƟz3qE\We 9<* MzSJ<$k৬iwr7o KyBF7e1{5 n 姾}1N `:\νoF&'=rt/}dޮ0>u,K<:ux.($& e'BܚZ_?έ h[-egURǜos".{ݳoDAbg}~=/ HPg'\im\AaVoڜ܅d $b A ,CD+PoZt]EQ ;1'w =0e1<0uPo};1y7F4 ㎏[ܮvU)ףOohcXrojUk{ k5rs+pY k}dBE!㔆*km= erSEIC8fC+0B/ፅl/|rMwKmtd1~=.}aQ Ӭ7s`~&o(̷6 قO0qJ[4&hi 18UҶZ%c 72&HÄN)0oSv t٘["E4AB)傝t B/k7n?aZjYpkғjj|smCkgU8q!ٸ8񗟳J]&8[ӏ^i$]ZÄ&Ls $D =YP0TYCu4#)#- QѠeL~[̼ 9ػv]}托 bm !,KPb̺ucLOǒnR%[AHݧ<2m茸㸄YY20P`(a$NJn$(z=UT⚨0>F{>$&J7~/v'9&(w3oe;/]8B Mk AƨBjcmϪ')#,z>o8 'j}YAk5F9Q. ]d?Ix\S_ҧ.OK(FBc( |h(+ھg$d@~h%(oi i VsWۜշ{W޴#TE (c9즴۝w8y%/򕾶Jd##|4R~PGsx֌'[ɳ2|LL"vzW ;aP~ҟ‚Oh=G7:ܞ^@ognM~W;ài1X0WUy3W{|ᜥ9ICXMY39Rm^ "_}bX5o#l~I$r=jz'>wKN*!mhwH^[n$—i_o!Ĭ$-!ѿC LtIy9 ,Xgp{-iƲkl>p`_l ܤw}tB 4p"M(Jĭ#ۛ۳}nf0k,Eeۅ_[2@IM5+ JK2gInzʥ< \e(< |Y^$#~rJ!Tq]YUOL ph c]4zq <'QI _[gQM-چ׎us {=Ȫ#W^2b0ĕR6hM/ 5L<&ט08Y̳~ݐAK&sHrXFO pخ"y[~&O^]c" 0q8X#hAnI n,FO# OZ^H%]$NPEy6[#^kXLYbwV!7ܹQ >XPQ_N2 =o _Ą7CR*jɢLvzӻ97%}W!o~h-ojs&D ^{vi0 n&[֑N+ x_+s(ws{i 9+阏~6]']ى{|a,xV#k֕!S-cs/jAco02cbXJߪdk[s;gڿkۗJoG˫D=txxG|[U^Z ҕ)B2qx?JBGvE珈Cy N#&Ux@-,XxFLB$)7Нƒaҭv pCNv1xrnulTjZ%CX(ԙHK(:UDI%_ͭ◖Fp4+VjS}ZԐ0d[Sܦ`U0[TEB)I"(&l:,x E j;XWip gUFBߡuZ3h\Xq}a{a $(LvP/iP| ~ꮚ§:ാZQN=jq%)7Za}x.zfE+OzMa.8f=BzlnnPT`TpIʂN|}5yh^쎂r$r2h_J.tBFv-ȋcg"?oį>+M5X2ÚbWVSO۩C'/5+~ȍw ¯)gV1d!od+cu0 eeYqOz뿢QdډAGe EHwǘy3]/K(>mkoѵM ATu?醃>n[FVXNW*/q>1kR R+_Q?z#hK¶ּU*7_a'# _fHH`9zYH簨BNO;.ܐnc0B<LK"&#QwJT s[gyx@Xgy\-"Q?oUG,rPi *[z o4h{z{e{֍T pX:D$r8WPUp8$J|/^?e{ߛy/Z-֔lXzӒ0#lYBJM)LaKY_J0bۛZ+k˗ ]IUc 0765}t _?@O@d#;[2 cǸH+| 8!:a\ł7/0ePH*|N'}'mDxEn@dbv$-4|̤uK”HWTG 't5׃<s\yֱ⎌x4(/S[Ǡ|I1tQA:/T-;VȄ4- ܇_]+}!#Q=?|WhZCk/)pf89$GW~c?˰ l4Γě[pBJy'*e2=,O'-D\QkIaLDUP/.DK[txYIG~*K01*D7m9`7 E7ۿv}c?.`2b0Vy/«RfdDuM@GqL_zy~! T,d%q4*2'X0L&yڠ_9Ê\hs kz`;!Y>qfbAhV +2~ZI'}b~qx̖zUpi]*O\l}PN:h^+-"+( 7殈Ps@ ^`r"4bBy\C]M|EYVרk7cpj. No7gAZ\p38StJc&6 B/MzM@ZF`㊱qWY){P,xȗT_'nN2Ldm_ gK\ (]SnHo_G;*Dy,_wDi{f$*Qv$Ɖ@1C68$@zoA0Ҍ׊Ng>εaDIo)k7cx>g:hXH#1=H~NWE%3е_u:2 tflbDcr૵kR%EZіZ-Nb>?9tw7B 6K Ψ6&,rGh lb&T&=Xk7]xZ;Z;jo>-R&.H r$I pJfû_㢈is H(/]etuo_ ,*/5Aa/*J CMKNx>cƘd C>jG;FRMw n <:Uo:&#&fBy5[u+ oU#BXQGy];.}t:lϜjŠgQ)ZR^$h Me@XSO)]N ?ɭv qY*~W A c4ۮ`{POq…g_HxU_=M`2fOL?֮C6lm׺ / j\U/FIf!h0yD:a#nԣ ĈR=w gjH a1w34$!嫊>׽w|IJ?5m3' ݸP8zm]I~Zt>yc$ESm +}M~NT+&[OPf@x&*uiD{íaA {|Nn/.4] ?ʷ.I cw+ Fo#*\d3YUΚ3&-ic}mvwÉג{tRkNƅx'L@!m%n9zԩ;jJ]mwqd;ϓO8l/ TX;I GhÈ:rb,RriĶa253e L"ɇ8);4o?6]xƼ0qFQw18 XҶ\ r3%6^<(NN$< w{6_ڇ}먓O [)Zֆ1_i1$3 ^Ʒ^8rp'G^ T*PeR[N+3 gU4Dď+*m70eCuBH#0O_r;8-4Pd^]nR"F#S*9&#d0;kډ7Yr4-O|=OMt],@ F ~BlACӑ N4Os SO-ڿgڅ _>ەQz!;UVҝɈKtJZ\M 6*Pi4MK"OEy^lEwt{>p^X1'O?N2ºj6V[?˟_z3kmZ BVbl^EܱVPG`kb2Or%?vM=dɟ4}b>)eO@:s*+ j-?*YNCO1ƈ&qYw Gr&e4> 7uzרYfzt'(ي.荞F +<̻g_& Ó1Efm~@Ì|ϯ)Bo͂3y踰K/7(|{ ЁgO;% H'-W}.gAWc.rWi(y@dgcXSTY@3Ls} w'ෟ?l{zoZDq)KKKo_ n(ҒWe,8~-:u1]$#l&x*3##/S[_)OBQ6+J9VYT[OI@J^eeˍ"D.TZ |X2e{Eo8+7QG~2+GDJ  M|g IT;Ψ=)!2`\YGa[?~Ĕ8'gth0x/9*B)J峰(8PDO Rt Όf{f}[7Y؀Ciɀv̫ \JQ(DҤ: OؠyDVlg_}dkghyw]B[B@ 8ii}]xA LybГf+de-+J%BmI^p${7LCkyJ'' Z8hML]I2uil HdRWn#ox'zсjDT">,rU>N`MQ!?k ˅seS`Flhm;bǭwwR6se3.qMwٛ?ecȥm5@"(k} ^#rqdokx'kaw}YþK9#?M7P^ H mM[-Hqx]xG:N1'*XSo?@`q OpyJ~pp-J$w( ]GtSHRLOx)򓒺Sgbplfz󷴷Yn4wNtG,;KI;eBo4ޅ==&Jy%bM#O?"~%wO8[+D~Jj# 9va^7;:#lʗBq-Q̀j{|JAnIAlmL,گmemhZ{Eݥ$OD@^V1Rm ইG֕,hڐx_.EHl34#}=иv8DShbZY|Jf'H|UÉQ&$W N4 տ\2 d`c-jY =U_;aP>\}[[7.C~Iw,\XG~L` J9&jP~~DS.O@vr:lϥFzH#pN^;NbM1i֓4Z3]$3 0E9hR& fm+d%LSxՃ#ĖKNziD:ħ vnz죟j/<ƨfݬ m;(lQw*G:U S0p"(ö9YO|e/k;䔊CWYucY9gQιQPYD p`qb&ӞMĕ`M4u?@k S (>uGHȃ+e'0 JOQ^]ڙ#SOw,ҏnRG_kn:|`z%*^m6ʰPO6RLȤhsy.a4syO%tNEcP(9HYLM;tue˗᪪=IU#Hi)پ]к#Zu QXJ*4\!7! 7&H<У"W}e*r%h&rp]RN-V\8$+rРnReő9*s_%>Q9}NGax`1'5=#l750:W%a|Z9p#,nx?7]ſW"p H(#^ꌫIgM䜘۷>zJ!TN? goRBI|sjRi^l<#gK6Lp9Rc(̩U$,2=WlD?/Lk8YCcocs$Jy2LD52GP`=UJVE H(f-$SNXtRcCrBBXyK!+-(Nݽ~{X;sm ?عloK9*d;'@eNZ8 |EVpkIaI:ظp~ηNmNU㭒1:/7]ݙOܜO(,:93A5̟=.}`u34|—lӆNP)yVd@W<9*ܛWAp@ƧŊ(\J&٤=ue}d{Zx)m?bF./t]ɫK;K.Pi7kO7ܶ ݀'Fj&*rB_)7]qK>j\!׻NӘeFJ:U%AҳW>КU4[,7`M7&„V}3yg/?2~ʉ<~2soo7BP貪hvBۧ>^`Jum-o0&kS*N6vD\O'+sՍv٦jTWێ&U޸eDߴœʅI >y__ˎBrBՒ)l=yy-#ԓ} rw[{Y* Ltҗ8a8sn BɑN/ W ~bCOgV =cYUfH˥9ʈ : 弙riH!C>t$rK~9x GNeVB>H. GaRіsYGAS\rqg^J9#DtiH着: J`O*0NC{5NZJI9XL=wGYTV%+/T\;YV^0q/4`mUDIz ތvn LR8<+.)NqqKgzk紝|I^oTy  dO?^L׃)OdC^{@3s?noX]Ծkon籊;XE1C1[vVEg֤2Y{rrSZ29p21'8iI&"g*ĿzHevLu@@2 ywҡLQQ,O ).H#2g)fj ;[{k/,ۉmaHtTUi3ji#8'@O6'˰& &#P Uzt5> 82@1V$Iʟ  ct lDLMj>9GzpJ b7b4w:/hO="EӨl6kۧ7Fv/ ˟vY| -$Wt]q(52b )68Eט|q+P-sIsnl'ۣ&I :WOZAv`&53A릡9p3(Fӫ"^j7Y=.A 8 [ƿ*opno;v gp2)-#j?C9p*lF+YMyWk'.2!xz=z{Lv))<|QbB<^6wVPӨо9 =8KvdB*,<;s4భddEG~a.vGAԸ' 3 "NYQٗ$-G$I^gȷT)1h^E+ k8wOxqa:呧$G>A͛f^k|pz{󝇬y)Nޚ' D'8|Qz2RSy9!raˑJ L+OXDt2ܞJFZK9O2'c,U%+1ٰN_"/yV+: XSFLKHJ03?:/'pD𓌔>Mʹ#ojҡn\/l;JC>y/@)h%ϸL*y0=zS&yS7َ"[i>CIk7_%+ߋE\q)psk)aoX('B& oY%`!^F6#'u=U_]w[SO=Cp| @`XMtFhp|lN'nl)eUL9 u~ʺ'l_oqy_fVAʟuGe+6dIsy'7bˏ?hdAY3!SЎK*rk\2Md{J̇>>>̾=~_ٱ'Eπki/hsV4fq\IpIW`H+^'}tpiW4P:˦Ʉf(sApC,4|w%޶s9]>qo<O=5nN998.!|Ĵ\i[/Y\S'*UH*bpʋܳM+<GYP%l.0C?yk{|魭қo|mgz/,ԙN ƌ|B ~ڙhH>|?kH^9vⒿN{[$JP& MΩ*QGu |f1RbO'%WJ|l@^v}L}2"$G51>܃c<}'*kgюEN-Rv!-Ib,x9]e>^31QWh(:|Eh$Xe'bk-CtYڨlCx\5s{( ֞"D* n:SvfۆS36 .*db_ʔFn58xF܌W7q؜OW><9=G-? :)iNF;no@NG0(P3vsjg^p5Ǎttc}p\Rhb's@cCYȘ|vɓ}~sZ_P2d+;X<#ӅyL ڋScuwkvkk|o9_{{ے9P&|ʳ\I'6@=tLyĐINjAdy4 T`8N5{$ J܆$frGƯ.r|0>t}1r_cw>j vlwy.6.3el*-LkCFۼ?pS>9*V+VkQ,o9-NyڏH)P>\9≰XcHRȘC/kid'>j|>\G)9q:X.T⤾MO|Б'a<c'tܹ;j:wwx&H-޸s:njqp0z:N,<7h(G8“)>DIp!zںϡ!!M-Ajh^vһ^rv4 V;4n|X h9߽1>xѧwƇ9O^O_v-|8)_`LsDt=vҘ7Nyck&~cBu,fXKo}$E@񵓧D.h dV)%CP% ׾ժ-x2oyIH1'J@*QC}`Wmi0uU:e} ]*'Rv页ScdE~.}QwҖ܊2 ܙH8;&_%U4mX?ƒ'VbG'#1hN| ܠսC | `B?;]%V|V{&8v0:TC? {+_~Ipف +[W̯{|84ŋC&Lj"GȐs̈R N,[6بnjBSp{UL^"2mLy8 ٔT*X^#8V礛39Ul=(TJKf.O ) 4Lf5K-w|=Nq`g;9wDߺ?Ɲc-qZ?PXL,UKcbCBS# 86Dyk.xX8"w0hx~FwQmvl\U5^O_;ׄy|䉱b&S*oO. )U$ Ġ鶕KTv+}KnYK#^r3qe<9;8֘:։r}PVZ1 &O)QA+|frPFBİP4:ʐɟ$e! DZ@{TJDF,ڕH©2@w 5(=@ؓ6tǷܵLx,}'9Ov%rrEw>3c//8:A4m7K/m^3YRvu`!pY 10@ B) \}h'__ӳ@iPCxVa*=/a$_ ֯"渫Z:>!kwǟ'Ǜ KG{9A5vZ&WO j8A6=iUW8eKWle&MKĉ )("l1!Sy&D$/ř'v d9(/bZ:i8>?~m^?;~]4tӸMK F[(x8>O&ӓY/9X 1Տu54x:F 젶yS: Cnؚ:|p*{C|exŸS-O+fp+ƟWAY\#w c@,hmZ IU\ϻ ~=qL;jF %Z  mos*׏dS y"2.ŧ$%TEo)C?sh2%1ci,m,p̖lR^6Pu Ta|\ l,ctK-DC%b%Z{P>^'%{oo팏;q;MYK" a`CDNG *?4w9XTU!1]A &ĕah~\Ba,ma1?ڄAϤӄ]J¦:o{h˒!U/ A|K@ֺ1H`Lκ1saΥ<$Hi}e=:ŏZd5 2SLUS3jD8u@ 2 ؞վ|Sɢ"uf1} TO >?aGڎds0~>&%c0$D7r2)aMٰpзΖ/I~ 2+2|mrM(ADsAR80`Rb/:G".S8#ŨdUu%\aP˲ hK>a:9yWm"m jG[V-rָ/ٶƔ fC}^U|w+`nΩ_[>R-sbX8B.6TEPmUQ 늧u 6ԥ9^8m@$:J`8vtp~Y8{kl7<4tgtti>TD5/~x͡;"&&[E%'AMEҝR G;;M|Ixwj74Pmh2mxE5BR}8*ҐƗs]$vUb ?q9C=bWJ1JyS֖<ȤT\I6&`2fs> I ~Ķפti3}Ү_iѥ`ؾeկ,w !4r^aGl/fVB9nƬCe(읿/40}7%CY;Ŝڳ9.$2# qG)GrMl2 Ė9 vw<1SذQةn㌗: TCg#Ҧuo0u܀ud? nB'0p*||5,d3 `6M4 R8}ͺm˶ۊp$Ad~KHNCpM[A{JpW]aaa]&H9.HqL>Wn"H e.0)7I_Nvdc2# Sƞ6wV!o6(㲫Ey/ (h9$bpe@S23c61웖őI@]4 eg;I8`!qW=O}R%AVl*KUe6HÖ] fl{d!\a_,d7F݁8 AFԳSRuh!I~|]H*րϠ)ćv戆X Jbs/qa*[*DWd߁gJѶB[\u⦌|mu\q^MLMSO17\?o|sc4 6:Yli$3L8 &}dbȤU@MTinyЏݓϝ'}n.}x- :uݚ!AL]۾ |>,CYVz}|N[61!{E/`8bj+9aePYOPe$i;4מf{-قG=g#tJ _ELZ/-)D#8,! 邶=T6%+[^26c',ڶP9M]h9NmnԸ܎YL|#$6Q叟"8yo՜y 0BsE\C:6bV]3I q\mSODSxqjh.%α@>] M]ߓ?/+O>' ޷ W>?^σܱծ'_kRd"6 ,{Bn%"?WZpG(g620<`|֯|`~v} j/@b1B6vk:<iE6A\.y(TE3.@m7ǐ+.#eB~A,P,}<=F/(5΂h"x%<T;H,m=2dc):Yix=9~?Ʈ#Ϋާp[K#W!sn}xJbHAd^SaiD^(FTTi?sHץ'zuo=7Ԅs[9Mt? (z_ pE/9ܦ13kΛs7p럫W~c .ĄS&C;ǯwO~?sy| ݣa%;8[ &F#RmP3Km.j/0p(ri=ba؆|#soi9`rN*nl DL9AО)뙺_E63օJs?ejiM96RjC| mV?=!ˮ~fI`&{9] x&j/4MƔCq(s\:>&IcvmnV YC<9>Z;m s3]5|$D|k""sco{g.*]2שC܇;3}]׉5O'|7M|Ƈul"wr&w˯ 밥E!3 ?CtG]dރ'n.hE0E\ d\S. qa pM<+_Hw`l9:nigßp)aha_YY.M:ZȬ_X4MIk)tHZrf{Eg}*J(YYQ7hPݖ LLK 6KF-X&BiO [vy蟓l϶'Bg\`eo >xBsnKǓ |ބ&'oaӅI[frJa,vtU`O#S3tL)|#^ œdt{>&Ƣ:Vok2;nd`+ DžDvpN=6&}0y{JyC;e4||G>CX4kpώ= Dj Ddnht55Z_Þ~{!wSw<}nc%Xy^]? Mrx< 1)8eTP&I%}8 2Nʶ`ׯqpkB~;7ǃKW G㟾0] HqW)?ǥ)WT$.(2,Vm81wIĵd1zDf GFqCniS!  ]p!lNއU2N]% cqAr]`M' [%91[fb!CYW$ $`x|f:B'}]CKyL◁)3Eb6vs|lik~ &Q6XG%y?_L*>v=Ǎ]x(A֪ ,׾*F;xn< @,@&%b1>Xhk|x_G frh@xΤ?-w% ^ n>xK}1}p @:Z(/٨XWܡ&A]u碑? ~3{{s^N j/8q9"ScAZH waVCd'SIZ*-')n쑱@π`bȨ ˖QqWEV86БGWi$ ahY6 Aߢ;4<=KvgG$eΗ5~)ei`Rr:ǎᲣ14V=\)>]d  ߅V:8S^4MbT[iyx~KOy\[4 ׸W`&}:" Xn+ 7O^xg-A ָ`/gƶ'4jSY CG<}ewhpτwo:- x W3 ;dw{9҄:9?¶>;=ܭcC 'vߎ@ wt~W)_f%]UŌ~FU8.lB*-YbDBp`C-pBCKT,J**(s02(I{xg m@fIL*Sre&Ѧ%}+gyVH\0W”"8,8snQ68~KDbôҧ},K_C "ёhLeLծ`ig! {㯾z}`U>TL*′r)] پɞy ȝgxE- ?{ɵqHmMEǢl[lwy=|{뤸>Ot%:#!Wjv-gɹ۔ h鼠)wڒ,ԉ-؎Tg.TN""%䓸|B+]Fu_[~G3?4v/X8jh/ѤQ[tsHK. pu:u8^o*b33Юy{ /''ɝc[~[ɖ&bl쌽G.xu/?C}/tP8:˄.i@y0y P UprR/O$P~g;%?+Ix1 7XD")ySw,U1:N 70TL۬#LnB/YBh_ nRw6A>[D۝փF~PET!&m,<mv>C-4c+NBJ >1ԓ>P@b9 шҤbO[(b|Q.#3ANeelÄ(=B-d1 ԉҊ@$n)\}[SM@ ==;?ƃ׾?[޸ Tg{c;OU|cQ ?;\]SZ pտk/DC"~Nȃ_ $$©qqp3n_cCO*? ztR F+" *!O`ȃ7Ll*!'% ڮd8yE1d t.eo fL364 ln؍u)at"ނCo͢跜/OV3_Ӿaa6̤)3㌎(CK|]Nu,bK+Y.)%969ǀY_1&lՓLdD;8;6m׭0c{@( T^X|])ׁo9k??N=5*6jnE _4:t3@d‡aF+5+BhH|ewߺ0n폋/'o ρv4ɫp;x8y'~ow; wǞG.{QW~{ǧ㇟?#/^,bxДUicKdaf?mENfUg"j\3`a);x* 2>PXLT%FxG3y.X[cׇ |g9eTB:> 5L"ÂS6!yQHY1@4}IKhmd(%Qa#͋׏se":uo7h% Y%g B=/WdFłmxhѣL~ BbhU>FoO$*b3KxU8Nx}z`] /Krhd4B0M0}DMQ ГaE!'&_РEsWK> ϼ=^W;ZI-P8:ȱDrqrW"coC - _5Pܵq8VWƿ/xZsʝC}LqMPԏP`)ӎ6h l 8N R5{\,VxX _ih;A mZT`k!T>qÅLsV\5ha&Yhu qQ<@xp_`[[;vlltؿh =N5h]+}8'l/O~=}~k/{D~)YHYi#Ath}RHN'W:2nE sNh<ͣM  xDVpߪw.y|UOҒsM7O 5m`F3 Ӟ3Q”ؔx$$Uޮ't.Tf%)Z2/@gd6hظf3وOn#/^rb޶*v ޷32'8Cx ?~zƗ>熸 z NO E W4<|Fh"ګvL$]?ׂWb?ݞ=M,.G*W{ʏw9x0W5_3.ѫcxyk<ug\BX_ /ˋDEA#~ԜPHk g?E ?<jw  ,iJyK"~'mcF?r9PƩ?߀M:@#rPVC(NM*FMN m! &;uXUb%祵݁ Cy!X:!ggEjLY$l״n+!LLNDnE(GTOY+}(Uȋq^dCEH!A&/(rO]e ھVFL]q\S XB#P|h2!?<^/NßV&[/wNN @ 8RiѰ%%'-Sc(O;0ɊɤO[Dtlfߒ6:rRXUxw Eх# n""$(u=S$6cu4id/ٮcɒf_y[lr^4,cp&I`9H[|-,WBi `:C5e6;p~y%E]kc)i'I)04aZw:%`R599V, "lV%K\j3=z}JłJǔo IɄLsX%UؠS<:T ~R,I4J#N2b;OG!MO~k.+_oK.z^5eO:@?Fc{t$Li_ߐ:^l/@,|v6,wWт$7F Z"1ڶBMsʨ%b(Hs>ZR*ҔEfM[󎲆wk/mmQV2OAyN ͢ ˰m2je()>R6g3" p)6 *O~?lgZ> IߦlzhҙMӸQUB`RMe1҉Ko;9ןCO~"%A,]jcbsAW uX+qdՄUjL@h3N>݌7u :G9qdįƮ&y~ƽ |H,7`}+6 J&Pe HKkM]aEum] 4VҸUfd$6 0R4e?uf=ne=sC۟_^׺Z6\v_jk4Ftc]%Wbiݦ$)n.^0x% sSadc̰m#$-LT~.OY3^a([AT2$#0 qdL}y;d<ǥ' XB8wk_7%7歝aڜy/h`bTN[2 Ce _?{yp%َ/Jt+cGx UJ=X}&6m gi/)*`lݔQ3x@:'#|B ye,,>la ? V9F&wstq-5՟c &3K-mBFf`'呹,C%B B>JHO^Hb8e ysx~ho2 __8>奱aAl _IN!Oә!C1FinyiJ#; 6q^O`v\XViKH` ;/x݁rF%&ޡZ|KYKڣک%_"']Y :^́fy.QWoZaρ;_iتp#艥[W^1e_PRrNŠFVQ- NĢ,n@ }FHJ&y_"*_I)lFg:6AV݊e"k `$ʳQ'd\x7wno f+/Wӡxe~._ә@y^Zwt^-tBtL1 ,ݟ/#BH}3IDMYjYWEwOʢhGW2LJ6 |8 "r|K!2<ݜm!8¦S`Bc[4]»riMT&@6Dxywx_x㋟G2s*]:` ؞<FSލm˯y Ym`8ct\x;>5' o3 o{?;nqv1HGYRuL@t~}en䋍mqiwwfJ)Pf@(GC9D,=;VEMm: Ɲͨ lƌ_ 6ѻ9L "uM N*ؤXl򑴏X qVPռR~\K'9zki(A 9"I03b>ʔ,7Q %_)^Alл f 2%8NaF=eL%M8u'cƉWg ֺ$.19Ji .?o&!F 3/~xVע_wx~e3gy":;/$s/:&tyG  Z x/^d,İ:'idmà cdʘj CSڥCE>Q"F ox4$8[ nPfvV]XƷs.v 9lQ hҚJy[32'A[v# ?3Ifqgp#g)8lYn2c*[QCApF&&e^BP%p02"hMj;$ 1/lgd9D&J((IkY>*|oxN|Kl C&~?+OWum /x<K+s̅gȬT}&xL^dU`_UcN-|&?xCňeoydcjqPPQ&;o3Oe%LvZT&[YsN"KIY9))X Y&2|L'cH㟨e`b6fLuX3)H3'TCq 9ga.DŽgP:OǼ ו>>A? {a]`bw{FIۦ{pK;jfIH1/b {&BN6D9bBs]-\D|HF MQz;ր|a1qCg6oy)_[J6IF0gP=.e1J/8C7ڹNHk~I Oo{y4H MS+'T81JL'|6ds۞UJdAI{1G"dz0^ZKcZێ]UHv,Qz"ϙ! a]v>ŒؽpU ߾9^ o-$Bh{CZ,|F\&X c`VHs%o~Yk*|2X' HV-†JJgm З&ib&NfHm0X[| p-!lr:F-M{#;01N6-BYh^0KƫdL{k]=|P: W"mVNՙv>NŌ,$m2fLbEn!KEߚ:o&ͤOtMO~c K3W0ޕ?EG%CclV_pmrF*R[>t [s^.$ I㝼la#cx蹰X聦kdd(]Ċ|C\% hd4- D+0O=yP$̹TB;2,dȉ@ Ȏ5 Q0dP I т|5f:۬O.N8zלil[T ĄMj]@&S˰}> v/m g  Tf?m}{/'Wxºx #x/7 x/ ^vvh{3zSRCPyXMѤAW/}S~kl mW13϶M>$c/Co-;BX_p ct1uvEd0,(zӾ{Mg(k.lRT:vp MW&)ݤY'>6*Hp}|E ! X&ʃ\'Ti<*wbLJwȨ~20R*:j|N&Ra>'ٻ0<'}& l`qpv_}iڗ 85v| ?7DǙ$a<ځuBuA$ >Yz6<"C3d3,.,!fB3B,LĔ]E4EUYL;R1tP閂*بt+0&KTv) Xu6}-Dι͐nDN60i ɾ ڳ/W ' {ǍW^oqk_$౳MbL̤ #/%z6 @Ϝ","bZͶ`DY>&84aA1vyHu!{i("386w 4"[71j[!ӓCN yRخSA&#>v>#Ǯ&&fΫϝ3Tv|h>l8Iyu^DztR惽cohh+#$IyZ qSWvu7q󵯌~wzK ɢ@xki+,Az34;xM@L(L m]*ad<4d{tMI( ɦ@"g Ol(5#3=?sIi~ܾw!/C}={ qM- ΍7o7i_hQ0v5v:#^yH~PC.hC&==IpPЪL`RVs, j=B7> iO9`rKu$)a7S6"T5T98EIl&Fmf ȳpwQݹϩWn^\0 #mW jcZe1-C`L䠬,8bwr;{{O+O~(|  ߷zMWƭ7^wz}jʄ YlJی=80| mZV=" :lDFk[K$Q bN &T 7wgQY"lLOJ&(;ھT (lagCJ!Y`$p(~:.{M`O6M|U$6NҘI=)eDagR^'zn_qa]-~K|W7_ட@3`GVXCa\@W$-c tN+ ZְwPp"')#e.r@%$0)l0[64gӇ ٖ,3X7Isb6S # hσx=p{HwDVdb<T(6NaL%Gؤ5Y-Yhı ݤlIOGAF;eE4QJߥ ^`b 5.aYϐDd3#qGysˣ5S Ou LM / ǏL=?;W?;.ig +vXbap֍qh|1Ӌ, kj^ 8^ |00n+1쪤bif )JxVY84WP,IR1li94A/<@t%m8HO8C"JEj/c9˦X:.M)K纵r -Ģ?ߩT^H?pG#M[觉^+c5__D/^~~`]mwo;8x ;oC0`ЋR˜H.bS$OX2ɲCt&Vl )᜴1 >1&"a:@ % ~yAڷ59'XcU&0Y/e@x-7nJYB.bLI[Fu&pb1M _dL={UDI~eMώKW}u~W·) ޳Bܾ1kapth_>qdͤŒ`deC4 W4w(+x+Ivjv_#od${3f& b59L1*]69n "fLHI̵?ۆ6Qf e2BbM%X~\ar~"X8h&tY L!m{q/_<.\yWVx/X} LJ^ @89:wn# \iɝق;x=`3@Lv&6! e9ze<$FPqLBAlRePVe3O( !>Qt9")€u"-L' \7UyMV"|{=\݋WT]? ٿk~8oO.Vxld߿3'G_;.'}Md|I'xzRj]wHAd۶t!XYo$IדLFZ vKEOPӱ/֢bBu\ښ~$-m*0G(U#_Mwv5J]{/kG$IEYa +|gGq?S,n¡Z00a 7v8WID1KR2jbENe `[K b\|7_n0ʢ,  G6eʼ{gwwN~ؕ?Yau 896h8w Ǚ$YTp &~\d0lH[6` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\JPype.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\JPype.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end jpype-1.5.0/doc/quickguide.py000066400000000000000000000465561453713375400161510ustar00rootroot00000000000000import textwrap footnotes = [] footnotecounter = 1 def section(Title=None, Desc=None): print("%s" % Title) print("-" * len(Title)) if Desc: print("%s" % Desc) print() print("+" + ("-" * 27) + "+" + ("-" * 57) + "+" + ("-" * 57) + "+") print("| %-25s | %-55s | %-55s |" % ("Description", "Java", "Python")) print("+" + ("=" * 27) + "+" + ("=" * 57) + "+" + ("=" * 57) + "+") def endSection(): print() global footnotes, footnotecounter for i in range(0, len(footnotes)): print(" .. [%d] %s" % (i + footnotecounter, footnotes[i])) footnotecounter += len(footnotes) footnotes = [] print() print() def java(s): return """ .. code-block:: java %s """ % s def python(s): return """ .. code-block:: python %s """ % s def entry(Desc=None, Java=None, Python=None, Notes=None): global footnotes, footnotecounter if not Java: Java = "" if not Python: Python = "None" if Notes: global footnotes if Notes in footnotes: Desc += " [%d]_" % (footnotes.index(Notes) + footnotecounter) else: Desc += " [%d]_" % (len(footnotes) + footnotecounter) footnotes.append(Notes) DescLines = textwrap.wrap(Desc, 25) DescLines.insert(0, "") JavaLines = Java.split("\n") PythonLines = Python.split("\n") while (len(DescLines) > 0 or len(JavaLines) > 0 or len(PythonLines) > 0): if len(DescLines) > 0: print("| %-25s |" % DescLines.pop(0), end="") else: print("| %-25s |" % "", end="") if len(JavaLines) > 0: print(" %-55s |" % JavaLines.pop(0), end="") else: print(" %-55s |" % "", end="") if len(PythonLines) > 0: print(" %-55s |" % PythonLines.pop(0)) else: print(" %-55s |" % "") print("+" + ("-" * 27) + "+" + ("-" * 57) + "+" + ("-" * 57) + "+") ######################################################## print(""" Java QuickStart Guide ===================== This is a quick start guide to using JPype with Java. This guide will show a series of snippets with the corresponding commands in both Java and Python for using JPype. The :doc:`userguide` and :doc:`api` have additional details on the use of the JPype module. JPype uses two factory classes (``JArray`` and ``JClass``) to produce class wrappers which can be used to create all Java objects. These serve as both the base class for the corresponding hierarchy and as the factory to produce new wrappers. Casting operators are used to construct specify types of Java types (``JObject``, ``JString``, ``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). Two special classes serve as the base classes for exceptions (``JException``) and interfaces (``JInterface``). There are a small number of support methods to help in controlling the JVM. Lastly, there are a few annotations used to create customized wrappers. For the purpose of this guide, we will assume that the following classes were defined in Java. We will also assume the reader knows enough Java and Python to be dangerous. """) print(""" .. code-block:: java package org.pkg; public class BaseClass { public void callMember(int i) {} } public class MyClass extends BaseClass { final public static int CONST_FIELD = 1; public static int staticField = 1; public int memberField = 2; int internalField =3; public MyClass() {} public MyClass(int i) {} public static void callStatic(int i) {} public void callMember(int i) {} // Python name conflict public void pass() {} public void throwsException() throws java.lang.Exception {} // Overloaded methods public void call(int i) {} public void call(double d) {} } """) ##################################################################################### section("Starting JPype", """ The hardest thing about using JPype is getting the jars loaded into the JVM. Java is curiously unfriendly about reporting problems when it is unable to find a jar. Instead, it will be reported as an ``ImportError`` in Python. These patterns will help debug problems with jar loading. Once the JVM is started Java packages that are within a top level domain (TLD) are exposed as Python modules allowing Java to be treated as part of Python. """ ) entry("Start Java Virtual Machine (JVM)", None, """ .. code-block:: python # Import module import jpype # Enable Java imports import jpype.imports # Pull in types from jpype.types import * # Launch the JVM jpype.startJVM() """) entry("Start Java Virtual Machine (JVM) with a classpath", None, """ .. code-block:: python # Launch the JVM jpype.startJVM(classpath = ['jars/*']) """) entry("Import default Java namespace", None, python("import java.lang"), "All ``java.lang.*`` classes are available.") entry("Add a set of jars from a directory", None, python('jpype.addClassPath("/my/path/*")'), "Must happen prior to starting the JVM") entry("Add a specific jar to the classpath", None, python("jpype.addClassPath('/my/path/myJar.jar')"), "Must happen prior to starting the JVM") entry("Print JVM CLASSPATH", None, """ .. code-block:: python from java.lang import System print(System.getProperty("java.class.path")) """, "After JVM is started") endSection() ##################################################################################### section("Classes/Objects", """ Java classes are presented wherever possible similar to Python classes. The only major difference is that Java classes and objects are closed and cannot be modified. As Java is strongly typed, casting operators are used to select specific overloads when calling methods. Classes are either imported using a module, loaded using ``JPackage`` or loaded with the ``JClass`` factory. """) # Importing entry("Import a class", java("import org.pkg.MyClass"), python("from org.pkg import MyClass"), "This will report an error if the class is not found.") entry("Import a class and rename", None, python("from org.pkg import MyClass as OurClass"), "This will report an error if the class is not found.") entry("Import multiple classes from a package", None, python("from org.pkg import MyClass, AnotherClass"), "This will report an error if the classes are not found.") entry("Import a java package for long name access", None, python("import org.pkg"), "Does not report errors if the package is invalid.") entry("Import a class static", java("import org.pkg.MyClass.CONST_FIELD"), python("from org.pkg.MyClass import CONST_FIELD"), 'Constants, static fields, and static methods can be imported.') entry("Import a class without tld", java("import zippy.NonStandard"), python("NonStandard = JClass('zippy.NonStandard')"), "``JClass`` loads any class by name including inner classes.") # Construction entry("Construct an object", java("MyClass myObject = new MyClass(1);"), python("myObject = MyClass(1)")) entry("Constructing a class with full class name", None, """ .. code-block:: python import org.pkg myObject = org.pkg.MyClass(args) """) # Fields entry("Get a static field", java("int var = MyClass.staticField;"), python("var = MyClass.staticField")) entry("Get a member field", java("int var = myObject.memberField;"), python("var = myObject.memberField")) entry("Set a static field", java("MyClass.staticField = 2;"), python("MyClass.staticField = 2"), "This produces an error for final fields.") entry("Set a member field", java("myObject.memberField = 2;"), python("myObject.memberField = 2"), "This produces an error for final fields.") # Methods entry("Call a static method", java("MyClass.callStatic(1);"), python("MyClass.callStatic(1)")) entry("Call a member method", java("myObject.callMember(1);"), python("myObject.callMember(1)")) entry("Access member with Python naming conflict", java("myObject.pass()"), python("myObject.pass_()"), "Underscore is added during wrapping.") entry("Checking inheritance", java("if (obj instanceof MyClass) {...}"), python("if (isinstance(obj, MyClass): ...")) entry("Checking if Java class wrapper", None, python("if (isinstance(obj, JClass): ...")) entry("Checking if Java object wrapper", None, python("if (isinstance(obj, JObject): ...")) # Casting entry("Casting to a specific type", java("BaseClass b = (BaseClass)myObject;"), python("b = (BaseClass) @ myObject"), "Matmul(@) is used as the casting operator.") endSection() ##################################################################################### section("Exceptions", """ Java exceptions extend from Python exceptions and can be dealt with in the same way as Python native exceptions. JException serves as the base class for all Java exceptions. """) entry("Catch an exception", """ .. code-block:: java try { myObject.throwsException(); } catch (java.lang.Exception ex) { ... } """, """ .. code-block:: python try: myObject.throwsException() except java.lang.Exception as ex: ... """) entry("Throw an exception to Java", """ .. code-block:: java throw new java.lang.Exception( "Problem"); """, """ .. code-block:: python raise java.lang.Exception( "Problem") """) entry("Checking if Java exception wrapper", None, python('if (isinstance(obj, JException): ...')) entry("Closeable items", """ .. code-block:: java try (InputStream is = Files.newInputStream(file)) { ... } """, """ .. code-block:: python with Files.newInputStream(file) as is: ... """) endSection() ##################################################################################### section("Primitives", """ Most Python primitives directly map into Java primitives. However, Python does not have the same primitive types, and it is necessary to cast to a specific Java primitive type whenever there are Java overloads that would otherwise be in conflict. Each of the Java types are exposed in JPype (``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). """) entry("Casting to hit an overload", java("myObject.call((int)v);"), python("myObject.call(JInt(v))"), "``JInt`` acts as a casting operator") entry("Create a primitive array", java("int[] array = new int[5]"), python("array = JInt[5]")) entry("Create a rectangular primitive array", java("int[][] array = new int[5][10]"), python("array = JInt[5, 10]")) entry("Create an array of arrays", java("int[][] array = new int[5][]"), python("array = JInt[5, :]")) entry("Create an initialized primitive array", java("int[] array = new int[]{1,2,3}"), python("array = JInt[:]([1,2,3])"), "list, sequences, or np.array can be used to initialize.") entry("Create an initialized boxed array", java("Integer[] array = new Integer[]{1,2,3}"), python("array = java.lang.Integer[:]([1,2,3])"), "list, sequences, or np.array can be used to initialize.") entry("Put a specific primitive type on a list", """ .. code-block:: java List myList = new ArrayList<>(); myList.add(1); """, """ .. code-block:: python from java.util import ArrayList myList = ArrayList() myList.add(JInt(1)) """) entry("Boxing a primitive", java("Integer boxed = 1;"), python("boxed = JObject(JInt(1))"), "``JInt`` specifies the prmitive type. ``JObject`` boxes the primitive.") endSection() ##################################################################################### section("Strings", """ Java strings are similar to Python strings. They are both immutable and produce a new string when altered. Most operations can use Java strings in place of Python strings, with minor exceptions as Python strings are not completely duck typed. When comparing or using as dictionary keys, all JString objects should be converted to Python. """) entry("Create a Java string", java('String javaStr = new String("foo");'), python( 'myStr = JString("foo")'), "``JString`` constructs a ``java.lang.String``") entry("Create a Java string from bytes", ''' .. code-block:: java byte[] b; String javaStr = new String(b, "UTF-8"); ''', ''' .. code-block:: python b= b'foo' myStr = JString(b, "UTF-8") ''', "All ``java.lang.String`` constuctors work.") entry("Converting Java string", None, python("str(javaStr)")) entry("Comparing Python and Java strings", None, python( "str(javaStr) == pyString"), "``str()`` converts the object for comparison") entry("Comparing Java strings", java( 'javaStr.equals("foo")'), python('javaStr == "foo"')) entry("Checking if Java string", None, python( "if (isinstance(obj, JString): ...")) endSection() ##################################################################################### section("Arrays", """ Arrays are create using the JArray class factory. They operate like Python lists, but they are fixed in size. """) entry("Create a single dimension array", java("MyClass[] array = new MyClass[5];"), python("array = MyClass[5]")) entry("Create a multi dimension array (old)", java("MyClass[][] array2 = new MyClass[5][];"), python("array2 = JArray(MyClass, 2)(5)")) entry("Create a multi dimension array (new)", java("MyClass[][] array2 = new MyClass[5][];"), python("array2 = MyClass[5,:]")) entry("Access an element", java("array[0] = new MyClass()"), python("array[0] = MyClass()")) entry("Size of an array", java("array.length"), python("len(array)")) entry("Get last element", java("MyClass a = array[array.length];"), python("a = array[-1]")) entry("Slice an array", None, python("a = array[2:5]"), "A Slice is a view and changes will be reflected on original. Slices passed to Java will clone.") entry("Clone an array", java("MyClass[] a = array.clone();"), python("a = array.clone()")) entry("Convert to Python list", None, python("pylist = list(array)")) entry("Iterate elements", """ .. code-block:: java for (MyClass element: array) {...} """, """ .. code-block:: python for element in array: ... """) entry("Checking if java array wrapper", None, python("if (isinstance(obj, JArray): ...")) endSection() ##################################################################################### section("Collections", """ Java standard containers are available and are overloaded with Python syntax where possible to operate in a similar fashion to Python objects. """) entry("Import list type", java("import java.util.ArrayList;"), python("from java.util import ArrayList")) entry("Construct a list", java("List myList=new ArrayList<>();"), python("myList=ArrayList()")) entry("Get length of list", java("int sz = myList.size();"), python("sz = len(myList)")) entry("Get list item", java('Integer i = myList.get(0)'), python('i = myList[0]')) entry("Set list item", java('myList.set(0, 1)'), python('myList[0]=Jint(1)'), "Casting is required to box primitives to the correct type.") entry("Iterate list elements", """ .. code-block:: java for (Integer element: myList) {...} """, """ .. code-block:: python for element in myList: ... """) entry("Import map type", java("import java.util.HashMap;"), python("from java.util import HashMap")) entry("Construct a map", java("Map myMap = new HashMap<>();"), python("myMap = HashMap()")) entry("Get length of map", java("int sz = myMap.size();"), python("sz = len(myMap)")) entry("Get map item", java('Integer i = myMap.get("foo")'), python('i = myMap["foo"]')) entry("Set map item", java('myMap.set("foo", 1)'), python('myMap["foo"] = Jint(1)'), "Casting is required to box primitives to the correct type.") entry("Iterate map entries", """ .. code-block:: java for (Map.Entry e : myMap.entrySet()) {...} """, """ .. code-block:: python for e in myMap.entrySet(): ... """) endSection() ##################################################################################### section("Reflection", """ Java reflection can be used to access operations that are outside the scope of the JPype syntax. This includes calling a specific overload or even accessing private methods and fields. """) entry("Access Java reflection class", java("MyClass.class"), python("MyClass.class_")) entry("Access a private field by name", None, """ .. code-block:: python cls = myObject.class_ field = cls.getDeclaredField( "internalField") field.setAccessible(True) field.get() """, """This is prohibited after Java 8""") entry("Accessing a specific overload", None, """ .. code-block:: python cls = MyClass.class_ cls.getDeclaredMethod("call", JInt) cls.invoke(myObject, JInt(1)) """, "types must be exactly specified.") entry("Convert a ``java.lang.Class`` into Python wrapper", None, """ .. code-block:: python # Something returned a java.lang.Class MyClassJava = getClassMethod() # Convert to it to Python MyClass = JClass(myClassJava) """, "Rarely required unless the class was supplied external such as generics.") entry("Load a class with a external class loader", """ .. code-block:: java ClassLoader cl = new ExternalClassLoader(); Class cls = Class.forName("External", True, cl) """, """ .. code-block:: python cl = ExternalClassLoader() cls = JClass("External", loader=cl) """) entry("Accessing base method implementation", None, """ .. code-block:: python from org.pkg import \\ BaseClass, MyClass myObject = MyClass(1) BaseClass.callMember(myObject, 2) """) endSection() section("Implements and Extension", """ JPype can implement a Java interface by annotating a Python class. Each method that is required must be implemented. JPype does not support extending a class directly in Python. Where it is necessary to exend a Java class, it is required to create a Java extension with an interface for each methods that are to be accessed from Python. """) entry("Implement an interface", """ .. code-block:: java public class PyImpl implements MyInterface { public void call() {...} } """, """ .. code-block:: python @JImplements(MyInterface) class PyImpl(object): @JOverride def call(self): pass """, "") entry("Extending classes", None, None, """Support for use of Python function as Java 8 lambda is WIP.""") entry("Lambdas", java('DoubleUnaryOperator u = (p->p*2);'), python('u=DoubleUnaryOperator@(lambda x: x*2)'), 'Any Java functional interface can take a lambda or callable.') endSection() print( """ Don't like the formatting? Feel the guide is missing something? Submit a pull request at the project page. """) jpype-1.5.0/doc/quickguide.rst000066400000000000000000002415671453713375400163300ustar00rootroot00000000000000 Java QuickStart Guide ===================== This is a quick start guide to using JPype with Java. This guide will show a series of snippets with the corresponding commands in both Java and Python for using JPype. The :doc:`userguide` and :doc:`api` have additional details on the use of the JPype module. JPype uses two factory classes (``JArray`` and ``JClass``) to produce class wrappers which can be used to create all Java objects. These serve as both the base class for the corresponding hierarchy and as the factory to produce new wrappers. Casting operators are used to construct specify types of Java types (``JObject``, ``JString``, ``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). Two special classes serve as the base classes for exceptions (``JException``) and interfaces (``JInterface``). There are a small number of support methods to help in controlling the JVM. Lastly, there are a few annotations used to create customized wrappers. For the purpose of this guide, we will assume that the following classes were defined in Java. We will also assume the reader knows enough Java and Python to be dangerous. .. code-block:: java package org.pkg; public class BaseClass { public void callMember(int i) {} } public class MyClass extends BaseClass { final public static int CONST_FIELD = 1; public static int staticField = 1; public int memberField = 2; int internalField =3; public MyClass() {} public MyClass(int i) {} public static void callStatic(int i) {} public void callMember(int i) {} // Python name conflict public void pass() {} public void throwsException() throws java.lang.Exception {} // Overloaded methods public void call(int i) {} public void call(double d) {} } Starting JPype -------------- The hardest thing about using JPype is getting the jars loaded into the JVM. Java is curiously unfriendly about reporting problems when it is unable to find a jar. Instead, it will be reported as an ``ImportError`` in Python. These patterns will help debug problems with jar loading. Once the JVM is started Java packages that are within a top level domain (TLD) are exposed as Python modules allowing Java to be treated as part of Python. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Start Java Virtual | | .. code-block:: python | | Machine (JVM) | | | | | | # Import module | | | | import jpype | | | | | | | | # Enable Java imports | | | | import jpype.imports | | | | | | | | # Pull in types | | | | from jpype.types import * | | | | | | | | # Launch the JVM | | | | jpype.startJVM() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Start Java Virtual | | .. code-block:: python | | Machine (JVM) with a | | | | classpath | | # Launch the JVM | | | | jpype.startJVM(classpath = ['jars/*']) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import default Java | | .. code-block:: python | | namespace [1]_ | | | | | | import java.lang | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Add a set of jars from a | | .. code-block:: python | | directory [2]_ | | | | | | jpype.addClassPath("/my/path/*") | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Add a specific jar to the | | .. code-block:: python | | classpath [2]_ | | | | | | jpype.addClassPath('/my/path/myJar.jar') | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Print JVM CLASSPATH [3]_ | | .. code-block:: python | | | | | | | | from java.lang import System | | | | print(System.getProperty("java.class.path")) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [1] All ``java.lang.*`` classes are available. .. [2] Must happen prior to starting the JVM .. [3] After JVM is started Classes/Objects --------------- Java classes are presented wherever possible similar to Python classes. The only major difference is that Java classes and objects are closed and cannot be modified. As Java is strongly typed, casting operators are used to select specific overloads when calling methods. Classes are either imported using a module, loaded using ``JPackage`` or loaded with the ``JClass`` factory. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Import a class [4]_ | .. code-block:: java | .. code-block:: python | | | | | | | import org.pkg.MyClass | from org.pkg import MyClass | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a class and rename | | .. code-block:: python | | [4]_ | | | | | | from org.pkg import MyClass as OurClass | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import multiple classes | | .. code-block:: python | | from a package [5]_ | | | | | | from org.pkg import MyClass, AnotherClass | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a java package for | | .. code-block:: python | | long name access [6]_ | | | | | | import org.pkg | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a class static | .. code-block:: java | .. code-block:: python | | [7]_ | | | | | import org.pkg.MyClass.CONST_FIELD | from org.pkg.MyClass import CONST_FIELD | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import a class without | .. code-block:: java | .. code-block:: python | | tld [8]_ | | | | | import zippy.NonStandard | NonStandard = JClass('zippy.NonStandard') | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Construct an object | .. code-block:: java | .. code-block:: python | | | | | | | MyClass myObject = new MyClass(1); | myObject = MyClass(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Constructing a class with | | .. code-block:: python | | full class name | | | | | | import org.pkg | | | | myObject = org.pkg.MyClass(args) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get a static field | .. code-block:: java | .. code-block:: python | | | | | | | int var = MyClass.staticField; | var = MyClass.staticField | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get a member field | .. code-block:: java | .. code-block:: python | | | | | | | int var = myObject.memberField; | var = myObject.memberField | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set a static field [9]_ | .. code-block:: java | .. code-block:: python | | | | | | | MyClass.staticField = 2; | MyClass.staticField = 2 | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set a member field [9]_ | .. code-block:: java | .. code-block:: python | | | | | | | myObject.memberField = 2; | myObject.memberField = 2 | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Call a static method | .. code-block:: java | .. code-block:: python | | | | | | | MyClass.callStatic(1); | MyClass.callStatic(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Call a member method | .. code-block:: java | .. code-block:: python | | | | | | | myObject.callMember(1); | myObject.callMember(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Access member with Python | .. code-block:: java | .. code-block:: python | | naming conflict [10]_ | | | | | myObject.pass() | myObject.pass_() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking inheritance | .. code-block:: java | .. code-block:: python | | | | | | | if (obj instanceof MyClass) {...} | if (isinstance(obj, MyClass): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java class | | .. code-block:: python | | wrapper | | | | | | if (isinstance(obj, JClass): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java object | | .. code-block:: python | | wrapper | | | | | | if (isinstance(obj, JObject): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Casting to a specific | .. code-block:: java | .. code-block:: python | | type [11]_ | | | | | BaseClass b = (BaseClass)myObject; | b = (BaseClass) @ myObject | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [4] This will report an error if the class is not found. .. [5] This will report an error if the classes are not found. .. [6] Does not report errors if the package is invalid. .. [7] Constants, static fields, and static methods can be imported. .. [8] ``JClass`` loads any class by name including inner classes. .. [9] This produces an error for final fields. .. [10] Underscore is added during wrapping. .. [11] Matmul(@) is used as the casting operator. Exceptions ---------- Java exceptions extend from Python exceptions and can be dealt with in the same way as Python native exceptions. JException serves as the base class for all Java exceptions. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Catch an exception | .. code-block:: java | .. code-block:: python | | | | | | | try { | try: | | | myObject.throwsException(); | myObject.throwsException() | | | } catch (java.lang.Exception ex) | except java.lang.Exception as ex: | | | { ... } | ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Throw an exception to | .. code-block:: java | .. code-block:: python | | Java | | | | | throw new java.lang.Exception( | raise java.lang.Exception( | | | "Problem"); | "Problem") | | | | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java | | .. code-block:: python | | exception wrapper | | | | | | if (isinstance(obj, JException): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Closeable items | .. code-block:: java | .. code-block:: python | | | | | | | try (InputStream is | with Files.newInputStream(file) as is: | | | = Files.newInputStream(file)) | ... | | | { ... } | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ Primitives ---------- Most Python primitives directly map into Java primitives. However, Python does not have the same primitive types, and it is necessary to cast to a specific Java primitive type whenever there are Java overloads that would otherwise be in conflict. Each of the Java types are exposed in JPype (``JBoolean``, ``JByte``, ``JChar``, ``JShort``, ``JInt``, ``JLong``, ``JFloat``, ``JDouble``). +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Casting to hit an | .. code-block:: java | .. code-block:: python | | overload [12]_ | | | | | myObject.call((int)v); | myObject.call(JInt(v)) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a primitive array | .. code-block:: java | .. code-block:: python | | | | | | | int[] array = new int[5] | array = JInt[5] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a rectangular | .. code-block:: java | .. code-block:: python | | primitive array | | | | | int[][] array = new int[5][10] | array = JInt[5, 10] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create an array of arrays | .. code-block:: java | .. code-block:: python | | | | | | | int[][] array = new int[5][] | array = JInt[5, :] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create an initialized | .. code-block:: java | .. code-block:: python | | primitive array [13]_ | | | | | int[] array = new int[]{1,2,3} | array = JInt[:]([1,2,3]) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create an initialized | .. code-block:: java | .. code-block:: python | | boxed array [13]_ | | | | | Integer[] array = new Integer[]{1,2,3} | array = java.lang.Integer[:]([1,2,3]) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Put a specific primitive | .. code-block:: java | .. code-block:: python | | type on a list | | | | | List myList | from java.util import ArrayList | | | = new ArrayList<>(); | myList = ArrayList() | | | myList.add(1); | myList.add(JInt(1)) | | | | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Boxing a primitive [14]_ | .. code-block:: java | .. code-block:: python | | | | | | | Integer boxed = 1; | boxed = JObject(JInt(1)) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [12] ``JInt`` acts as a casting operator .. [13] list, sequences, or np.array can be used to initialize. .. [14] ``JInt`` specifies the prmitive type. ``JObject`` boxes the primitive. Strings ------- Java strings are similar to Python strings. They are both immutable and produce a new string when altered. Most operations can use Java strings in place of Python strings, with minor exceptions as Python strings are not completely duck typed. When comparing or using as dictionary keys, all JString objects should be converted to Python. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Create a Java string | .. code-block:: java | .. code-block:: python | | [15]_ | | | | | String javaStr = new String("foo"); | myStr = JString("foo") | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a Java string from | .. code-block:: java | .. code-block:: python | | bytes [16]_ | | | | | byte[] b; | b= b'foo' | | | String javaStr = new String(b, "UTF-8"); | myStr = JString(b, "UTF-8") | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Converting Java string | | .. code-block:: python | | | | | | | | str(javaStr) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Comparing Python and Java | | .. code-block:: python | | strings [17]_ | | | | | | str(javaStr) == pyString | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Comparing Java strings | .. code-block:: java | .. code-block:: python | | | | | | | javaStr.equals("foo") | javaStr == "foo" | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if Java string | | .. code-block:: python | | | | | | | | if (isinstance(obj, JString): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [15] ``JString`` constructs a ``java.lang.String`` .. [16] All ``java.lang.String`` constuctors work. .. [17] ``str()`` converts the object for comparison Arrays ------ Arrays are create using the JArray class factory. They operate like Python lists, but they are fixed in size. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Create a single dimension | .. code-block:: java | .. code-block:: python | | array | | | | | MyClass[] array = new MyClass[5]; | array = MyClass[5] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a multi dimension | .. code-block:: java | .. code-block:: python | | array (old) | | | | | MyClass[][] array2 = new MyClass[5][]; | array2 = JArray(MyClass, 2)(5) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Create a multi dimension | .. code-block:: java | .. code-block:: python | | array (new) | | | | | MyClass[][] array2 = new MyClass[5][]; | array2 = MyClass[5,:] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Access an element | .. code-block:: java | .. code-block:: python | | | | | | | array[0] = new MyClass() | array[0] = MyClass() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Size of an array | .. code-block:: java | .. code-block:: python | | | | | | | array.length | len(array) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get last element | .. code-block:: java | .. code-block:: python | | | | | | | MyClass a = array[array.length]; | a = array[-1] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Slice an array [18]_ | | .. code-block:: python | | | | | | | | a = array[2:5] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Clone an array | .. code-block:: java | .. code-block:: python | | | | | | | MyClass[] a = array.clone(); | a = array.clone() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Convert to Python list | | .. code-block:: python | | | | | | | | pylist = list(array) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Iterate elements | .. code-block:: java | .. code-block:: python | | | | | | | for (MyClass element: array) | for element in array: | | | {...} | ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Checking if java array | | .. code-block:: python | | wrapper | | | | | | if (isinstance(obj, JArray): ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [18] A Slice is a view and changes will be reflected on original. Slices passed to Java will clone. Collections ----------- Java standard containers are available and are overloaded with Python syntax where possible to operate in a similar fashion to Python objects. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Import list type | .. code-block:: java | .. code-block:: python | | | | | | | import java.util.ArrayList; | from java.util import ArrayList | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Construct a list | .. code-block:: java | .. code-block:: python | | | | | | | List myList=new ArrayList<>(); | myList=ArrayList() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get length of list | .. code-block:: java | .. code-block:: python | | | | | | | int sz = myList.size(); | sz = len(myList) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get list item | .. code-block:: java | .. code-block:: python | | | | | | | Integer i = myList.get(0) | i = myList[0] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set list item [19]_ | .. code-block:: java | .. code-block:: python | | | | | | | myList.set(0, 1) | myList[0]=Jint(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Iterate list elements | .. code-block:: java | .. code-block:: python | | | | | | | for (Integer element: myList) | for element in myList: | | | {...} | ... | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Import map type | .. code-block:: java | .. code-block:: python | | | | | | | import java.util.HashMap; | from java.util import HashMap | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Construct a map | .. code-block:: java | .. code-block:: python | | | | | | | Map myMap = new HashMap<>(); | myMap = HashMap() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get length of map | .. code-block:: java | .. code-block:: python | | | | | | | int sz = myMap.size(); | sz = len(myMap) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Get map item | .. code-block:: java | .. code-block:: python | | | | | | | Integer i = myMap.get("foo") | i = myMap["foo"] | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Set map item [19]_ | .. code-block:: java | .. code-block:: python | | | | | | | myMap.set("foo", 1) | myMap["foo"] = Jint(1) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Iterate map entries | .. code-block:: java | .. code-block:: python | | | | | | | for (Map.Entry e | for e in myMap.entrySet(): | | | : myMap.entrySet()) | ... | | | {...} | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [19] Casting is required to box primitives to the correct type. Reflection ---------- Java reflection can be used to access operations that are outside the scope of the JPype syntax. This includes calling a specific overload or even accessing private methods and fields. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Access Java reflection | .. code-block:: java | .. code-block:: python | | class | | | | | MyClass.class | MyClass.class_ | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Access a private field by | | .. code-block:: python | | name [20]_ | | | | | | cls = myObject.class_ | | | | field = cls.getDeclaredField( | | | | "internalField") | | | | field.setAccessible(True) | | | | field.get() | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Accessing a specific | | .. code-block:: python | | overload [21]_ | | | | | | cls = MyClass.class_ | | | | cls.getDeclaredMethod("call", JInt) | | | | cls.invoke(myObject, JInt(1)) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Convert a | | .. code-block:: python | | ``java.lang.Class`` into | | | | Python wrapper [22]_ | | # Something returned a java.lang.Class | | | | MyClassJava = getClassMethod() | | | | | | | | # Convert to it to Python | | | | MyClass = JClass(myClassJava) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Load a class with a | .. code-block:: java | .. code-block:: python | | external class loader | | | | | ClassLoader cl | cl = ExternalClassLoader() | | | = new ExternalClassLoader(); | cls = JClass("External", loader=cl) | | | Class cls | | | | = Class.forName("External", | | | | True, cl) | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Accessing base method | | .. code-block:: python | | implementation | | | | | | from org.pkg import \ | | | | BaseClass, MyClass | | | | myObject = MyClass(1) | | | | BaseClass.callMember(myObject, 2) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [20] This is prohibited after Java 8 .. [21] types must be exactly specified. .. [22] Rarely required unless the class was supplied external such as generics. Implements and Extension ------------------------ JPype can implement a Java interface by annotating a Python class. Each method that is required must be implemented. JPype does not support extending a class directly in Python. Where it is necessary to exend a Java class, it is required to create a Java extension with an interface for each methods that are to be accessed from Python. +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | Description | Java | Python | +===========================+=========================================================+=========================================================+ | | | | | Implement an interface | .. code-block:: java | .. code-block:: python | | | | | | | public class PyImpl | @JImplements(MyInterface) | | | implements MyInterface | class PyImpl(object): | | | { | @JOverride | | | public void call() | def call(self): | | | {...} | pass | | | } | | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | None | | Extending classes [23]_ | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ | | | | | Lambdas [24]_ | .. code-block:: java | .. code-block:: python | | | | | | | DoubleUnaryOperator u = (p->p*2); | u=DoubleUnaryOperator@(lambda x: x*2) | | | | | +---------------------------+---------------------------------------------------------+---------------------------------------------------------+ .. [23] Support for use of Python function as Java 8 lambda is WIP. .. [24] Any Java functional interface can take a lambda or callable. Don't like the formatting? Feel the guide is missing something? Submit a pull request at the project page. jpype-1.5.0/doc/release.rst000066400000000000000000000032161453713375400156010ustar00rootroot00000000000000:orphan: Release cycle docs ================== This project uses bump2version See https://medium.com/@williamhayes/versioning-using-bumpversion-4d13c914e9b8 To start a new cycle use: ``bumpversion patch`` To increment the build number during development: ``bumpversion build`` To release: ``bumpversion release`` Full process: (first copy the checklist to an issue) - [ ] Start from the release branch ``git checkout release`` - [ ] Make a new branch for the release cycle ``git checkout -b releases/{version}`` - [ ] Check the .azure scripts to see if they are up to date. Look on https://devguide.python.org/versions/ to see what versions can be dropped Check Python versions for Windows Check Python versions for OSX Check the manylinux image for Linux - [ ] Merge the current master with the release ``git pull origin master`` - [ ] Start a release ``bumpversion release`` - [ ] Edit doc/CHANGELOG.rst - [ ] Send the release to be evaluated ``git push`` - [ ] Verify CI on azure - [ ] Manually trigger a ``jpype.release`` on azure If successful, download the artifacts for publication. - [ ] Advance the release pointer ``git checkout release`` ``git merge releases/`` - [ ] Publish the release - Add draft release on github - Attach the artifacts to the release. - [ ] Start master on a new cycle - Use a PR to pull release back to master - ``git checkout master`` - ``git checkout -b cycle`` - ``git merge release`` - ``bumpversion patch`` - Use PR to insert the cycle in master **Last, update this document with any changes in process that were required.** jpype-1.5.0/doc/userguide.rst000066400000000000000000004475261453713375400161750ustar00rootroot00000000000000################## JPype User Guide ################## .. toctree:: :maxdepth: 2 JPype Introduction ****************** JPype is a Python module to provide full access to Java from within Python. Unlike Jython, JPype does not achive this by re-implementing Python, but instead by interfacing both virtual machines at the native level. This shared memory based approach achieves good computing performance, while providing the access to the entirety of CPython and Java libraries. This approach allows direct memory access between the two machines, implementation of Java interfaces in Python, and even use of Java threading. JPype Use Cases =============== Here are three typical reasons to use JPype. - Access to a Java library from a Python program (Python oriented) - Visualization of Java data structures (Java oriented) - Interactive Java and Python development including scientific and mathematical programming. Let's explore each of these options. Case 1: Access to a Java library -------------------------------- Suppose you are a hard core Python programmer. You can easily use lambdas, threading, dictionary hacking, monkey patching, been there, done that. You are hard at work on your latest project but you just need to pip in the database driver for your customers database and you can call it a night. Unfortunately, it appears that your customers database will not connect to the Python database API. The whole thing is custom and the customer isn't going to supply you with a Python version. They did send you a Java driver for the database but fat lot of good that will do for you. Stumbling through the internet you find a module that says it can natively load Java packages as Python modules. Well, it's worth a shot... So first thing the guide says is that you need to install Java and set up a ``JAVA_HOME`` environment variable pointing to the JRE. Then start the JVM with classpath pointed to customers jar file. The customer sent over an example in Java so you just have to port it into Python. .. code-block:: java package com.paying.customer; import com.paying.customer.DataBase public class MyExample { public void main(String[] args) { Database db = new Database("our_records"); try (DatabaseConnection c = db.connect()) { c.runQuery(); while (c.hasRecords()) { Record record = db.nextRecord(); ... } } } } It does not look too horrible to translate. You just need to look past all those pointless type declarations and meaningless braces. Once you do, you can glue this into Python and get back to what you really love, like performing dictionary comprehensions on multiple keys. You glance over the JPype quick start guide. It has a few useful patterns... set the class path, start the JVM, remove all the type declarations, and you are done. .. code-block:: python # Boiler plate stuff to start the module import jpype import jpype.imports from jpype.types import * # Launch the JVM jpype.startJVM(classpath=['jars/database.jar']) # import the Java modules from com.paying.customer import DataBase # Copy in the patterns from the guide to replace the example code db = Database("our_records") with db.connect() as c: c.runQuery() while c.hasRecords(): record = db.nextRecord() ... Launch it in the interactive window. You can get back to programming in Python once you get a good night sleep. Case 2: Visualization of Java structures ---------------------------------------- Suppose you are a hard core Java programmer. Weakly typed languages are for wimps, if it isn't garbage collected it is garbage. Unfortunately your latest project has suffered a nasty data structure problem in one of the threads. You managed to capture the data structure in a serialized form but if you could just make graph and call a few functions this would be so much easier. But the interactive Java shell that you are using doesn't really have much in the way of visualization and your don't have time to write a whole graphing applet just to display this dataset. So poking around on the internet you find that Python has exactly the visualization that you need for the problem, but it only runs in CPython. So in order to visualize the structure, you need to get it into Python, extract the data structures and, send it to the plotting routine. You install conda, follow the install instructions to connect to conda-forge, pull JPype1, and launch the first Python interactive environment that appear to produce a plot. You get the shell open and paste in the boilerplate start commands, and load in your serialized object. .. code-block:: python import jpype import jpype.imports jpype.startJVM(classpath = ['jars/*', 'test/classes']) from java.nio.file import Files, Paths from java.io import ObjectInputStream with Files.newInputStream(Paths.get("myobject.ser")) as stream: ois = ObjectInputStream(stream) obj = ois.readObject() print(obj) # prints org.bigstuff.MyObject@7382f612 It appears that the structure is loaded. The problematic structure requires you call the getData method with the correct index. .. code-block:: python d = obj.getData(1) > TypeError: No matching overloads found for org.bigstuff.MyObject.getData(int), > options are: public double[] org.bigstuff.MyObject.getData(double) public double[] org.bigstuff.MyObject.getData(int) Looks like you are going to have to pick the right overload as it can't figure out which overload to use. Darn weakly typed language, how to get the right type in so that you can plot the right data. It says that you can use the casting operators. .. code-block:: python from jpype.types import * d = obj.getData(JInt(1)) print(type(d)) # prints double[] Great. Now you just need to figure out how to convert from a Java array into our something our visualization code can deal with. As nothing indicates that you need to convert the array, you just copy out of the visualization tool example and watch what happens. .. code-block:: python import matplotlib.pyplot as plt plt.plot(d) plt.show() A graph appears on the screen. Meaning that NumPy has not issue dealing with Java arrays. It looks like ever 4th element in the array is zero. It must be the PR the new guy put in. And off you go back to the wonderful world of Java back to the safety of curly braces and semicolons. Case 3: Interactive Java ------------------------ Suppose you are a laboratory intern running experiments at Hawkins National Laboratory. (For the purpose of this exercise we will ignore the fact that Hawkins was shut down in 1984 and Java was created in 1995). You have the test subject strapped in and you just need to start the experiment. So you pull up Jupyter notebook your boss gave you and run through the cells. You need to added some heart wave monitor to the list of graphed results. The relevant section of the API for the Experiment appears to be .. code-block:: java package gov.hnl.experiment; public interface Monitor { public void onMeasurement(Measurement measurement); } public interface Measurement { public double getTime(); public double getHeartRate(); public double getBrainActivity(); public double getDrugFlowRate(); public boolean isNoseBleeding(); } public class Experiment { public void addCondition(Instant t, Condition c); public void addMoniter(Monitor m); public void run(); } The notebook already has all the test conditions for the experiment set up and the JVM is started, so you just need to implement the monitor. Based on the previous examples, you start by defining a monitor class .. code-block:: python from jpype import JImplements, JOverride from gov.hnl.experiment import Monitor @JImplements(Monitor) class HeartMonitor: def __init__(self): self.readings = [] @JOverride def onMeasurement(self, measurement): self.readings.append([measurement.getTime(), measurement.getHeartRate()]) def getResults(self): return np.array(self.readings) There is a bit to unpack here. You have implemented a Java class from within Python. The Java implementation is simply an ordinary Python class which has be decorated with ``@JImplements`` and ``@JOverride``. When you forgot to place the ``@JOverride``, it gave you the response:: NotImplementedError: Interface 'gov.hnl.experiment.Monitor' requires method 'onMeasurement' to be implemented. But once you added the ``@JOverride``, it worked properly. The subject appears to be getting impatient so you hurry up and set up a short run to make sure it is working. .. code-block:: python hm = HeartMonitor() experiment.addMonitor(hm) experiment.run() readings = hm.getResults() plt.plot(readings[:,0], readings[:,1) plt.show() To your surprise, it says unable to find method addMonitor with an error message:: AttributeError: 'gov.hnl.experiment.Experiment' object has no attribute 'addMonitor' You open the cell and type ``experiment.add``. The line completes with ``experiment.addMoniter``. Whoops, looks like there is typo in the interface. You make a quick correction and see a nice plot of the last 30 seconds pop up in a window. Job well done, so you set the runtime back to one hour. Looks like you still have time to make the intern woodlands hike and forest picnic. Though you wonder if maybe next year you should sign up for another laboratory. Maybe next year, you will try to sign up for those orbital lasers the President was talking about in the March. That sounds like real fun. (This advanced demonstration utilized the concept of Proxies_ and `Code completion`_) The JPype Philosophy ==================== JPype is designed to allow the user to exercise Java as fluidly as possible from within Python. We can break this down into a few specific design goals. - Make Java appear Pythonic. Make it so a Python programmer feels comfortable making use of Java concepts. This means making use of Python concepts to create very Python looking code and at times bending Python concepts to conform to Java's expectations. - Make Python appear like Java. Present concepts from Java with a syntax that resembles Java so that Java users can work with Python without a huge learning curve. - Present everything that Java has to offer to Python. Every library, package, and Java feature if possible should be accessible. The goal of bridge is to open up places and not to restrict flow. - Keep the design as simple as possible. Mixing languages is already complex enough so don't required the user to learn a huge arsenal of unique methods. Instead keep it simple with well defined rules and reuse these concepts. For example, all array types originate from JArray, and thus using one can also use isinstance to check if a class is an array type. Rather than introducing factory that does a similar job to an existing one, instead use a keyword argument on the current factory. - Favor clarity over performance. This doesn't mean not trying to optimize paths, but just as premature optimization is the bane of programmers, requiring writing to maximize speed is a poor long term choice, especially in a language such as Python where weak typing can promote bit rot. - If a new method has to be introduced, make it look familiar. Java programmers look to a method named "of" to convert to a type on factories such as a Stream, thus ``JArray.of`` converts a Python NumPy array to Java. Python programmers expect that memory backed objects can be converted into bytes for rapid transfer using a memory view, thus ``memoryview(array)`` will perform that task. - Provide an obvious way for both Python and Java programmers to perform tasks. On this front JPype and Python disagree. In Python's philosophy there should be one -- and preferably only one -- obvious way to do things. But we are bridging two worlds and thus obviousness is in the eye of the beholder. The end result is that JPype has a small footprint while providing access to Java (and other JVM based languages) with a minimum of effort. Languages other than Java ========================= JPype is primarily focused on providing the best possible wrapper for Java in Python. However, the Java Virtual Machine (JVM) is used for many popular languages such a Kotlin and Scala. As such JPype can be used for any language which used the JVM. That said each language has its own special properties that tend to be represented in different ways. If you would like JPype fully to operate on your particular language the following is required. - Set up a test bench for your language under the test directory. Use ivy to pull in the required jar files required to run it and exercise each of the required language features that need to be exercised. - Write a language specific quick start guide for your language defining how things should appear in both your language of choice and within Python highlighting those things that are different from how Java. - Set up a test harness that exercises your language for each language feature and place a setup script like ``test_java`` that builds the harness. Alternatives ============ JPype is not the only Python module of its kind that acts as a bridge to Java. Depending on your programming requirements, one of the alternatives may be a better fit. Specifically JPype is designed for clarity and high levels of integration between the Python and Java virtual machine. As such it makes use of JNI and thus inherits all of the benefits and limitations that JNI imposes. With JPype, both virtual machines are running in the same process and are sharing the same memory space and threads. JPype can thus intermingle Python and Java threads and exchange memory quickly. But by extension you can't start and stop the JVM machine but instead must keep both machines throughout the lifespan of the program. High integration means tightly coupled and thus it embodies the musketeers motto. If Python crashes, so does Java as they only have one process to live in. A few alternatives with different philosophies and limitations are given in the following section. Please take my review comments with the appropriate grain of salt. When I was tasked with finding a replacement for Matlab Java integration for our project test bench, I evaluated a number of alternatives Python bridge codes. I selected JPype primarily because it presented the most integrated API and documentation which would be suitable for getting physicists up to speed quickly. Thus your criteria may yield a different selection. JPype's underlying technology was underwhelming so I have had the pleasure of many hours reworking stuff under the hood. For more details on what you can't do with JPype, please see Limitations_. `Jython `_ ------------------------------- Jython is a reimplementation of Python in Java. As a result it has much lower costs to share data structures between Java and Python and potentially much higher level of integration. Noted downsides of Jython are that it has lagged well behind the state of the art in Python; it has a limited selection of modules that can be used; and the Python object thrashing is not particularly well fit in Java virtual machine leading to some known performance issues. `Py4J `_ --------------------------- Py4J uses a remote tunnel to operate the JVM. This has the advantage that the remote JVM does not share the same memory space and multiple JVMs can be controlled. It provides a fairly general API, but the overall integration to Python is as one would expect when operating a remote channel operating more like an RPC front-end. It seems well documented and capable. Although I haven't done benchmarking, a remote access JVM will have a transfer penalty when moving data. `Jep `_ ------------------------------------- Jep stands for Java embedded Python. It is a mirror image of JPype. Rather that focusing on accessing Java from within Python, this project is geared towards allowing Java to access Python as sub-interpreter. The syntax for accessing Java resources from within the embedded Python is quite similar with support for imports. Notable downsides are that although Python supports multiple interpreters many Python modules do not, thus some of the advantages of the use of Python many be hard to realize. In addition, the documentation is a bit underwhelming thus it is difficult to see how capable it is from the limited examples. `PyJnius `_ -------------------------------------------- PyJnius is another Python to Java only bridge. Syntax is somewhat similar to JPype in that classes can be loaded in and then have mostly Java native syntax. Like JPype, it provides an ability to customize Java classes so that they appear more like native classes. PyJnius seems to be focused on Android. It is written using Cython .pxi files for speed. It does not include a method to represent primitive arrays, thus Python list must be converted whenever an array needs to be passed as an argument or a return. This seems pretty prohibitive for scientific code. PyJnius appears is still in active development. `Javabridge `_ ------------------------------------------------------------------ Javabridge is direct low level JNI control from Python. The integration level is quite low on this, but it does serve the purpose of providing the JNI API to Python rather than attempting to wrap Java in a Python skin. The downside being of course you would really have to know a lot of JNI to make effective use of it. `jpy `_ ------------------------------------- This is the most similar package to JPype in terms of project goals. They have achieved more capabilities in terms of a Java from Python than JPype which does not support any reverse capabilities. It is currently unclear if this project is still active as the most recent release is dated 2014. The integration level with Python is fairly low currently though what they do provide is a similar API to JPype. `JCC `_ ------------------------------------------------ JCC is a C++ code generator that produces a C++ object interface wrapping a Java library via Java's Native Interface (JNI). JCC also generates C++ wrappers that conform to Python's C type system making the instances of Java classes directly available to a Python interpreter. This may be handy if your goal is not to make use of all of Java but rather have a specific library exposed to Python. `VOC _` ---------------------------------------------------------- A transpiler that converts Python bytecode into Java bytecode part of the BeeWare project. This may be useful if getting a smallish piece of Python code hooked into Java. It currently list itself as early development. This is more in the reverse direction as its goals are making Python code available in Java rather providing interaction between the two. `p2j `_ ---------------------------------------------- This lists itself as "A (restricted) python to java source translator". Appears to try to convert Python code into Java. Has not been actively maintained since 2013. Like VOC this is primilarly for code translation rather that bridging. About this guide ================ The JPype User Guide is targeted toward programmers who are strong in either Python who wish to make use of Java or those who are strong with Java and are looking to use Python as a Java development tool. As such we will compare and contrast the differences between the languages and provide examples suitable to help illustrate how to translate from one language to the other on the assumption that being strong in one language will allow you to easily grasp the corresponding relations in the other. If you don't have a strong background in either language an appropriate language tutorial may be necessary. JPype will hide virtually all of the JNI layer such that there is no direct access to JNI concepts. As such attempting to use JNI knowledge will likely lead to incorrect assumptions such as incorrectly attempting to use JNI naming and method signatures in the JPype API. Where JNI limitations do appear we will discuss the consequences imposed in programming. No knowledge of JNI is required to use this guide or JPype. JPype only works with Python 3, thus all examples will be using Python version 3 syntax and assume the use of the Python 3 new style object model. The naming conventions of JPype follow the Java rules rather than those of Python. This is a deliberate choice as it would be dangerous to try to mangle Java method and field names into Python conventions and risk a name collision. Thus if method must have Java conventions then the rest of the module should follow the same pattern for consistency. Getting JPype started --------------------- This document holds numerous JPype examples. For the purposes of clarity the module is assumed to have been started with the following command .. code-block:: python # Import the module import jpype # Allow Java modules to be imported import jpype.imports # Import all standard Java types into the global scope from jpype.types import * # Import each of the decorators into the global scope from jpype import JImplements, JOverride, JImplementationFor # Start JVM with Java types on return jpype.startJVM(convertStrings=False) # Import default Java packages import java.lang import java.util This is not the only style used by JPype users. Some people feel it is best to limit the number for symbols in the global scope and instead start with a minimalistic approach. .. code-block:: python import jpype as jp # Import the module jp.startJVM(convertStrings=False) # Start the module Either style is usable and we do not wish to force any particular style on the user. But as the extra ``jp.`` tends to just clutter up the space and implies that JPype should always be used as a namespace due to namespace conflicts, we have favored the global import style. JPype only exposes 40 symbols total including a few deprecated functions and classes. The 13 most commonly used Java types are wrapped in a special module ``jpype.types`` which can be used to import all for the needed factories and types with a single command without worrying about importing potentially problematic symbols. We will detail the starting process more later in the guide. See `Starting the JVM`_. JPype Concepts *************** At its heart, JPype is about providing a bridge to use Java within Python. Depending on your perspective that can either be a means of accessing Java libraries from within Python or a way to use Java using Python syntax for interactivity and visualization. This mean not only exposing a limited API but instead trying to provide the entirety of the Java language with Python. To do this, JPype maps each of the Java concepts to the nearest concept in Python wherever they are similar enough to operate without confusion. We have tried to keep this as Pythonic as possible, though it is never without some rough edges. Python and Java share many of the same concepts. Types, class, objects, function, methods, and members. But in other places they are rather different. Python lacks casting, type declarations, overloading, and many other features of a strongly typed language, thus we must expose those concepts into the Python syntax as best we can. Java for instance has class annotation and Python have class decorators. Both serve the purpose of augmenting a class with further information, but are very different in execution. We have broken the mapping down in nine distinct concepts. Some elements serve multiple functions. Type Factories These are meta classes that allow one to declare a particular Java type in Python. The result of type factories are wrapper classes. (JClass_ and JArray_) Factories also exist to implement Java classes from within Python (JProxy_) Meta Classes These are classes to describe different properties of Java classes such as to check if a class is an Interface. (JInterface_) Base Classes These are JPype names for Java classes in Python that exist without importing any specific Java class. Concepts such as Object, String, and Exception are defined and can be used in instance checks. For example, to catch all Java exceptions regardless of type, we would catch ``JException``. These are mainly for convenience though they do have some extra functionality. Most of these functions are being phased out in favor of Java syntax. For example, catching ``java.lang.Throwable`` will catch everything that ``JException`` will catch. (Jarray_, JObject_, JString_, and JException_) Wrapper Classes These correspond to each Java class. Thus can be used to access static variables, static methods, cast, and construct object. They are used wherever a Java type would be used in the Java syntax such as creating an array or accessing the class instance. These class wrappers are customized in Python to allow a direct mapping from Java concepts to Python one. These are all created dynamically corresponding to each Java class. For most of this document we will refer to these simply as a "class". (`java.lang.Object`_, `java.lang.String`_, etc) Many wrappers are customized to match Python abstract base classes ABC (`java.util.List`_, `java.util.Map`_) Object Instances These are Java objects. They operate just like Python objects with Java public fields mapped to Python attributes and Java methods to Python methods. For this document we will refer to an object instance simply as an "object". The object instance is split into two halves. The Python portion is referred to as the "handle" that points the Java "instance". The lifetime of the "instance" is tied to the handle thus Java objects do not disappear until the Python handle is disposed of. Objects can be cast_ to match the required type and hold methods_ and fields. `Primitive types`_ Each of the 8 Java primitive types are defined. These are used to cast to a Java type or to construct arrays. (`JBoolean`_, `JChar`_, `JByte`_, `JShort`_, `JInt`_, `JLong`_, `JFloat`_, and `JDouble`_) Decorators Java has a number of keywords such as extending a class or implementing an interface. Those pieces of meta data can't directly be expressed with the Python syntax, but instead have been been expressed as annotations that can be placed on classes or functions to augment them with Java specific information. (`@JImplements`_, `@JOverride`_, `@JImplementationFor`_) Mapping Java syntax to Python Many Java concepts like try with resources can be mapped into Python directly (as the ``with`` statement), or Java try, throw, catch mapping to Python try, raise, except. Others such as synchronize do not have an exact Python match. Those have instead been mapped to special functions that interact with Python syntax.. (synchronized_, `with`, `try`, import_) JVM control functions The JVM requires specific actions corresponding to JNI functions in order to start, shutdown, and define threading behavior. These top level control functions are held in the ``jpype`` module. (startJVM_, shutdownJVM_) We will detail each of these concepts in greater detail in the later sections. Name mangling ============= When providing Java package, classes, methods, and fields to Python, there are occasionally naming conflicts. For example, if one has a method called ``with`` then it would conflict with the Python keyword ``with``. Wherever this occurs, JPype renames the offending symbol with a trailing under bar. Java symbols with a leading or trailing under bars are consider to be privates and may not appear in the JPype wrapper entirely with the exception of package names. The following Python words will trigger name mangling of a Java name: =========== =========== ============= =========== ========== ``False`` ``None`` ``True`` ``and`` ``as`` ``async`` ``await`` ``def`` ``del`` ``elif`` ``except`` ``exec`` ``from`` ``global`` ``in`` ``is`` ``lambda`` ``nonlocal`` ``not`` ``or`` ``pass`` ``print`` ``raise`` ``with`` ``yield`` =========== =========== ============= =========== ========== JPype Types *********** Both Java and Python have a concept of a type. Every variable refers to an object which has a defined type. A type defines the data that the variable is currently holding and how that variable can be used. In this chapter we will learn how Java and Python types relate to one another, how to create import types from Java, and how to use types to create Java objects. Stay strong in a weak language ============================== Before we get into the details of the types that JPype provides, we first need to contrast some of the fundamental language differences between Java and Python. Python is inherently a weakly typed language. Any variable can take any type and the type of a particular variable can change over the lifetime of a program. Types themselves can be mutable as you can patch an existing type to add new behaviors. Python methods can in principle take any type of object as an argument, however if the interface is limited it will produce a TypeError to indicate a particular argument requires a specific type. Python objects and classes are open. Each class and object is basically a dictionary storing a set of key value pairs. Types implemented in native C are often more closed and thus can't have their method dictionaries or data members altered arbitrarily. But subject to a few restrictions based implementation, it is pretty much the wild west. In contrast, Java is a strongly typed language. Each variable can only take a value of the specified class or a class that derives from the specified class. Each Java method takes only a specific number and type of arguments. The type and number are all checked at compile type to ensure there is little possibility of error. As each method requires a specific number and type of arguments, a method can be overloaded by having two different implementations which take a different list of types sharing the same method name. A primitive variable can never hold an object and it can only be converted to or from other primitive types unless it is specifically cast to that type. Java objects and classes are completely closed. The methods and fields for a particular class and object are defined entirely at compile time. Though it is possible create classes with a dictionary allowing expansion, this is not the Java norm and no standard mechanism exists. Thus we need to introduce a few Java terms to the Python vocabulary. These are "conversion" and "cast". Java conversions ---------------- A conversion is a permitted change from an object of one type to another. Conversions have three different degrees. These are: exact, derived, implicit, and explicit. Exact conversions are those in which the type of an object is identical. In Java each class has only one definition thus there is no need for an exact conversion. But when dealing with Python we have objects that are effectively identical for which exact conversion rules apply. For example, a Java string and a Python string both bind equally well to a method which requires a string, thus this is an exact conversion for the purposes of bind types. The next level of conversion is derived. A derived class is one which is a descends from a required type. It is better that implicit but worse than exact. If all of the types in a method match are exact or derived then it will override a method in which one argument is implicit. The next level of conversion is implicit. An implicit conversion is one that Java would perform automatically. Java defines a number of other conversions such as converting a primitive to a boxed type or from a boxed type back to a primitive as implicit conversions. Python conversions defined by the user are also considered to be implicit. Of course not every cast is safe to perform. For example, converting an object whose type is currently viewed as a base type to a derived type is not performed automatically nor is converting from one boxed type to another. For those operations the conversion must be explicitly requested, hence these are explicit conversions. In Java, a cast is requested by placing the type name in parentheses in front of the object to be cast. Python does not directly support Java casting syntax. To request an explicit conversion an object must be "cast" using a cast operator @. Overloaded methods with an explicit argument will not be matched. After applying an explicit cast, the match quality can improve to exact or derived depending on the cast type. Not every conversion is possible between Java types. Types that cannot be converted are considerer to be conversion type "none". Details on the standard conversions provided by JPype are given in the section `Type Matching`_. .. _cast: Java casting ------------ To access a casting operation we use the casting ``JObject`` wrapper. For example, ``JObject(object, Type)`` would produce a copy with specificed type. The first argument is the object to convert and the second is the type to cast to. The second argument should always be a Java type specified using a class wrapper, a Java class instance, or a string. Casting will also add a hidden class argument to the resulting object such that it is treated as the cast type for the duration of that variable lifespan. Therefore, a variable create by casting is stuck as that type and cannot revert back to its original for the purposes of method resolution. The object construction and casting are sometimes a bit blurry. For example, when one casts a sequence to a Java list, we will end up constructing a new Java list that contains the elements of the original Python sequence. In general JPype constructors only provide access the Java constructor methods that are defined in the Java documentation. Casting on the other hand is entirely the domain of whatever JPype has defined including user defined casts. As ``JObject`` syntax is long and does not look much like Java syntax, the Python matmul operator is overloaded on JPype types such that one can use the ``@`` operator to cast to a specific Java type. In Java, one would write ``(Type)object`` to cast the variable ``object`` to ``Type``. In Python, this would be written as ``Type@object``. This can also be applied to array types ``JLong[:]@[1,2,3]``, collection types ``Iterable@[1,2,3]`` or Java functors ``DoubleUnaryOperator@(lambda x:x*2)``. The result of the casting operator will be a Java object with the desired type or raise a ``TypeError`` if the cast or conversion is not possible. For Python objects, the Java object will generally be a copy as it is not possible to reflect changes in an array back to Python. If one needs to retrieve the resulting changes keep a copy of the converted array before passing it. For an existing Java object, casting changes the resolution type for the object. This can be very useful when trying to call a specific method overload. For example, if we have a Java ``a=String("hello")`` and there were an overload of the method ``foo`` between ``String`` and ``Object`` we would need to select the overload with ``foo(java.lang.Object@a)``. .. _JObject: Casting is performed through the Python class ``JObject``. JObject is called with two arguments which are the object to be cast and the type to cast too. The cast first consults the conversion table to decide if the cast it permitted and produces a ``TypeError`` if the conversion is not possible. ``JObject`` also serves as a abstract base class for testing if an object instance belongs to Java. All objects that belong to Java will return true when tested with ``isinstance``. Like Python's sequence, JObject is an abstract base class. No classes actual derive from ``JObject``. .. _null: Of particular interest is the concept of Java ``null``. In Java, null is a typeless entity which can be placed wherever an object is taken to indicate that the object is not available. The equivalent concept in Python is ``None``. Thus all methods that accept any object type that permit a null will accept None as an augment with implicit conversion. However, sometime it is necessary to pass an explicit type to the method resolution. To achieve this in JPype use ``Type@None`` which will create a null pointer with the desired type. To test if something is null we have to compare the handle to None. This unfortunately trips up some code quality checkers. The idiom in Python is ``obj is None``, but as this only matches things that Python considers identical, we must instead use ``obj==None``. Casting ``None`` is use to specify types when calling between overloads with variadic arguments such as ``foo(Object a)`` and ``foo(Object... many)``. If we want to call ``foo(None)`` is is ambiguous whether we intend to call the first with a null object or the second with a null array. We can resolve the ambiguity with ``foo(java.lang.Object@None)`` or ``foo(java.lang.Object[:]@None)`` Type enforcement appears in three different places within JPype. These are whenever a Java method is called, whenever a Java field is set, and whenever Python returns a value back to Java. .. _methods: Method resolution ================= Because Java supports method overloading and Python does not, JPype wraps Java methods as a "method dispatch". The dispatch is a collection of all of the methods from class and all of its parents which share the same name. The job of the dispatch is chose the method to call. Enforcement of the strong typing of Java must be performed at runtime within Python. Each time a method is invoked, JPype must match against the list of all possible methods that the class implements and choose the best possible overload. For this reason the methods that appear in a JPype class will not be the actual Java methods, but rather a "dispatch" whose job is deciding which method should be called based on the type of the provided arguments. If no method is found that matches the provided arguments, the method dispatch will produce a ``TypeError``. This is the exact same outcome that Python uses when enforcing type safety within a function. If a type doesn't match a ``TypeError`` will be produced. Dispatch example ---------------- When JPype is unable to decide which overload of a method to call, the user must resolve the ambiguity. This is where casting comes in. Take for example the ``java.io.PrintStream`` class. This class has a variant of the print and println methods! So for the following code: .. code-block:: python java.lang.System.out.println(1) JPype will automatically choose the ``println(long)`` method, because the Python int matches exactly with the Java long, while all the other numerical types are only "implicit" matches. However, if that is not the version you wanted to call you must cast it. In this case we will use a primitive type to construct the correct type. Changing the line thus: .. code-block:: python java.lang.System.out.println(JByte(1)) # <--- wrap the 1 in a JByte This tells JPype to choose the byte version. When dealing with Java types, JPype follows the standard Java matching rules. Types can implicitly grow to larger types but will not shrink without an explicit cast. Primitive Types =============== Unlike Python, Java makes a distinction between objects and primitive data types. Primitives represent the minimum data that can be manipulated by a computer. These stand in contrast to objects which have the ability to contain any combination of data types and object within themselves, and can be inherited from. Java primitives come in three flavors. The logical primitive ``boolean`` can only take the logical value true and false. The textual primitive ``char`` represents one character in a string. Numerical primitives are intended for fixed point or floating point calculations. Numerical primitives come in many sizes depending on how much storage is required. In Java, integer numerical primitives are always signed and thus can only reach half their range in terms of bits up or down relative to their storage size. JPype has mapped each of the primitive types into Python classes. To avoid conflicts with Python, JPype has named each primitive with a capital letter ``J`` followed by the primitive name starting with an upper case letter. .. _JBoolean: JBoolean A boolean is the logical primitive as it can only take values ``True`` and ``False``. It should properly be an extension of the Python concept ``bool`` but that type is not extendable. Thus instead it must inherit from ``int``. This type is rarely seen in JPype as the values ``True`` and ``False`` are considered an exact match to ``JBoolean`` argument. Methods which return a ``JBoolean`` will always return a Python ``bool`` rather than a Java primitive type. .. _JChar: JChar A character is the textual primitive that corresponds to exactly one character in a string. Or at least that was the concept at the time. Java characters can only represent 16 bits. But there are currently 143,924 defined characters in Unicode. Thus, there are certain characters that can only be represented as two Unicode characters. The textual primitives are not intended to perform numerical functions, but are instead encoded. As per the old joke, what does `1` plus `1` equal? Which of course the correct answer is `b`. As such characters should not be treated as just another unsigned short. Python has no concept of a textual only type. Thus when returning a character type, we instead return a string length 1. ``JChar`` supports the Java numerical operations, but just as in Java it will automatically promote to a Python ``int`` when used in a numerical operation. There are of course lots of useful mathematical operations that can be performed on textual primitives, but doing so risks breaking the encoding and can result in uninterpretable data. .. _JByte: .. _JShort: .. _JInt: .. _JLong: JByte, Short, Int, Long These types represent fixed point quantities with ranges of 8, 16, 32, and 64 bits. Each of these type inherit from a Python ``int`` type. A method or field returning an integer primitive will return a type derived from ``int``. Methods accepting an integer primitive will take either an Java integer primitive or a Python ``int`` or anything that quacks like a ``int`` so long as it can be converted into that primitive range without truncation. .. _JFloat: .. _JDouble: JFloat, JDouble These two types hold floating point and correspond to either single point (32 bit) or double point (64 bit) precision. Python does not have a concept of precision and thus both of these derive from the Python type ``float``. As per Java rules numbers greater than the range correspond to the values of positive and negative infinity. Conversions from Python types are ranged check and will produce a ``OverflowError`` if the value doesn't fit into the request types. If an overflow error is not desired, first cast the value into the request size prior to calling. Methods that return a Java floating point primitive will always return a value derived from ``float``. The classes for Java primitives are closed and should not be extended. As with all Java values any information attached to the Python representation is lost when passing that value to Java. Objects & Classes ================= In contrast to primitive data type, objects can hold any combination of primitives or objects. Thus they represent structured data. Objects can also hold methods which operate on that data. Objects can inherit from one another. However unlike Python, Java objects must have a fixed structure which defines its type. These are referred to the object's class. Here is a point of confusion. Java has two different class concepts: the class definition and the class instance. When you import a class or refer to a method using the class name you are accessing the class definition. When you call ``getClass`` on an object it returns a class instance. The class instance is a object whose structure can be used to access the data and methods that define the class through reflection. The class instance cannot directly access the fields or method within a class but instead provides its own interface for querying the class. For the purposes of this document a "class" will refer to the class definition which corresponds to the Python concept of a class. Wherever the Java reflection class is being referred to we will use the term "class instance". The term "type" is synonymous with a "class" in Java, though often the term "type" is only used when inclusively discussing the type of primitives and objects, while the term "class" generally refers to just the types associated with objects. All objects in Java inherit from the same base class ``java.lang.Object``, but Java does not support multiple inheritance. Thus each class can only inherit from a single parent. Multiple inheritance, mix-ins, and diamond pattern are not possible in Java. Instead Java uses the concept of an interface. Any Java class can inherit as many interfaces as it wants, but these interfaces may not contain any data elements. As they do not contain data elements there can be no ambiguity as to what data a particular lookup. .. _JInterface: The meta class ``JInterface`` is used to check if a class type is an interface using ``isinstance``. Classes that are pure interfaces cannot be instantiated, thus, there is not such thing as an abstract instance. Therefore, every Java object should have Objects cannot actual be pure interfaces. To represent this in Python every interface inherits ``java.lang.Object`` methods even through it does not have ``java.lang.Object`` as a parent. This ensures that anonymous classes and lambdas have full object behavior. Classes ------- In JPype, Java classes are instances of the Python ``type`` and function like any ordinary Python class. However unlike Python types, Java classes are closed and cannot be extended. To enforce extension restrictions, all Java classes are created from a special private meta class called ``_jpype._JClass``. This gatekeeper ensures that the attributes of classes cannot be changed accidentally nor extended. The type tree of Java is fixed and closed. All Java classes have the following functionality. Class constructor The class constructor is accessed by using the Python call syntax ``()``. This special method invokes a dispatch whenever the class is called as a function. If an matching constructor is found a new Java instance is created and a Python handle to that instance is returned. In the case of primitive types, the constructor creates a Java value with the exact type requested. Get attribute The Python ``.`` operator gets an attribute from a class with a specified name. If no method or field exists a ``AttributeError`` will be raised. For public static methods, the getattr will produce a Python descriptor which can be called to invoke the static method. For public static fields, a Python descriptor will be produced that allows the field to be get or set depending on whether the field is final or not. Public instance methods and instance fields will produce a function that can be applied to a Java object to execute that method or access the field. Function accessors are non-virtual and thus they can provide access to behaviors that have been hidden by a derived class. Set attribute In general, JPype only allows the setting of public non-final fields. If you attempt to set any attribute on an object that does not correspond to a settable field it will produce an ``AttributeError``. There is one exception to this rule. Sometime it is necessary to attach addition private meta data to classes and objects. Attributes that begin with an underbar are consider to be Python private attributes. Private attributes handled by the default Python attribute handler allowing these attributes to be attached to to attach data to the Python handle. This data is invisible to Java and it is retained only on the Python instance. If an object with Python meta data is passed to Java and Java returns the object, the new Python handle will not contain any of the attached data as this data was lost when the object was passed to Java. ``class_`` Attribute For Java classes there is a special attribute called ``class``. This is a keyword in Python so `name mangling`_ applies. This is a class instance of type ``java.lang.Class``. It can be used to access fields and methods. Inner classes For methods and fields, public inner classes appear as attributes of the class. These are regular types that can be used to construct objects, create array, or cast. String The Java method ``toString`` is mapped into the Python function ``str(obj)``. Equality The Java method ``equals()`` has been mapped to Python ``==`` with special augmentations for null pointers. Java ``==`` is not exposed directly as it would lead to numerous errors. In principle, Java ``==`` should map to the Python concept of ``is`` but it is not currently possible to overload Python in such a way to achieve the desired effect. Hash The Java method ``hashCode`` is mapped to Python ``hash(obj)`` function. There are special augmentations for strings and nulls. Strings will return the same hash code as returned by Python so that Java strings and Python strings produce the same dictionary lookups. Null pointers produce the same hash value as None. Java defines ``hashCode`` on many objects including mutable ones. Often the ``hashCode`` for a mutable object changes when the object is changed. Only use immutable Java object (String, Instant, Boxed types) as dictionary keys or risk undefined behavior. Java objects are instances of Java classes and have all of the methods defined in the Java class including static members. However, the get attribute method converts public instance members and fields into descriptors which act on the object. Now that we have defined the basics of Java objects and classes, we will define a few special classes that operate a bit differently. Array Classes ------------- In Java all arrays are also objects, but they cannot define any methods beyond a limited set of Java array operations. These operations have been mapped into Python to their closest Python equivalent. Arrays also have a special type factory to produce them. In principle one can create an array class using ``JClass`` but the signature required would need to use the proper name as required for the Java method ``java.lang.Class.forName``. Instead we call the factory to create a new type to use. .. _JArray: The signature for JArray is ``JArray(type, [dims=1])``. The type argument accepts any Java type including primitives and constructs a new array class. This class can be used to create new instances, cast, or as the input to the array factory. The resulting object has a constructor method which take either a number, which is the desired size of the array, or a sequence which hold the elements of the array. If the members of the initializer sequence are not Java members then each will be converted. If any element cannot be converted a ``TypeError`` will be raised. As a shortcut the ``[]`` operator can be used to specify an array type or an array instance. For example, ``JInt[5]`` will allocate an array instance of Java ints with length 5. ``JInt[:]`` will create a type instance with an unspecific length which can be used for the casting operator. To create an array instance with multiple dimensions we would use ``JInt[5,10]`` which would create a rectangular array which was 5 by 10. To create a jagged array we would substitute ``:`` for the final dimensions. So ``JInt[5,:]`` is a length 5 array of an array of ``int[]``. Multidimensional array types are specificed like ``JInt[:,:,:]`` would be a Java type ``int[][][]``. This applied to both primitive and object types. JArray is an abstract base class for all Java classes that are produced. Thus, one can test if something is an array class using ``issubclass`` and if Java object is an array using ``isinstance``. Java arrays provide a few additional Python methods: Get Item Arrays are of course a collection of elements. As such array elements can be accessed using the Python ``[]`` operator. For multidimensional arrays JPype uses Java style access with a series of index operations such as ``jarray[4][2]`` rather than NumPy like multidimensional access. Get Slice Arrays can be accessed using a slice like a Python list. The slice operator is ``[start:stop:step]``. It should be noted that array slice are in fact views to the original array so any alteration to the slice will affect the original array. Array slices are cloned when passed back to Java. To force a clone immediately, use the ``clone`` method. Please note that applying the slice operator to a slice produces a new slice. Thus there can sometimes be an ambiguity between multidimensional access and repeated slicing. Set Item Array items can be set using the Python ``[]=`` operator. Set Slice Multiple array items can be set using a slice assigned with a sequence. The sequence must have the same length as the slice. If this condition is not met, an exception will be raised. If the items to be transferred are a buffer, then a faster buffer transfer assignment will be used. When buffer transfers are used individual elements are not checked for range, but instead cast just like NumPy. Thus, if we have the elements we wish to assign to the array contained within a NumPy array named ``na`` we can transfer all of them using ``jarray[:] = na``. Buffer transfer Buffer transfers from a Java array also work for primitive types. Thus we can simply call the Python ``memoryview(jarray)`` function to create a buffer that can be used to transfer any portion of a Java array out. Memory views of Java arrays are not writable. For each Java arrays can be used as the input to a Python for statement. To iterate each element use ``for elem in jarray:``. They can also be used in list comprehensions. Clone Java arrays can be duplicated using the method clone. To create a copy call ``jarray.clone()``. This operates both on arrays and slice views. Length Arrays in Java have a defined an immutable length. As such the Python ``len(array)`` function will produce the array length. However, as that does not match Java expectations, JPype also adds an attribute for length so that Java idiom ``jarray.length`` also works as expected. In addition, the Java class ``JChar[]`` has some addition customizations to help work better with string types. Java arrays are currently missing some of the requirements to act as a ``collections.abc.Sequence``. When working with Java arrays it is also useful to use the Java array utilities class ``java.util.Arrays`` as it has many methods that provide additional functionality. Java arrays do not support any additional mathematical operations at this time. Creating a Java array is also required when pass by reference syntax is required. For example, if a Java function takes an array, modifies it and we want to retrieve those values. In Java, all parameters are pass by value, but the contents of a container like an array can be modified which gives the appearance of pass by reference. For example. .. code-block:: java public void modifies(int[] v) { for (int i=0; i