pax_global_header00006660000000000000000000000064147507261110014516gustar00rootroot0000000000000052 comment=af93462fba7c026e8a8065a4af3b650c6a04546d spyder-kernels-3.0.3/000077500000000000000000000000001475072611100144705ustar00rootroot00000000000000spyder-kernels-3.0.3/.codecov.yml000066400000000000000000000004721475072611100167160ustar00rootroot00000000000000codecov: branch: master coverage: precision: 2 round: down range: "70...100" status: project: default: threshold: 5% patch: no changes: no parsers: gcov: branch_detection: conditional: yes loop: yes method: no macro: no comment: false spyder-kernels-3.0.3/.coveragerc000066400000000000000000000004151475072611100166110ustar00rootroot00000000000000[run] omit = # Omit tests */tests/* [report] # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain if non-runnable code isn't run if __name__ == .__main__.: spyder-kernels-3.0.3/.gitattributes000066400000000000000000000115021475072611100173620ustar00rootroot00000000000000# Set the default behavior, in case people don't have core.autocrlf set. * text eol=lf # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. *.txt text # Source code *.c text *.cpp text *.inc text *.ipynb text *.h text *.py text *.py3 text *.pxd text *.pyw text *.pyx text *.qss text *.r text *.R text *.rb text *.Rmd text *.Rnw text *.sh text # Documentation *.adoc text *.latex text *.LaTeX text *.markdown text *.md text *.po text *.pot text *.rst text *.tex text *.TeX text *.tmpl text *.tpl text # Web *.atom text *.css text *.htm text *.html text *.js text *.jsx text *.json text *.php text *.pl text *.rss text *.sass text *.scss text *.xht text *.xhtml text # Configuration *.cfg text *.cnf text *.conf text *.config text *.desktop text *.inf text *.ini text *.plist text *.toml text *.xml text *.yml text *.yaml text # Plain text data *.cdl text *.csv text *.dif text *.geojson text *.gml text *.kml text *.Rdata text *.RData text *.sql text *.tab text *.tsv text *.wkt text # Special files .*rc text .checkignore text .ciocheck text .ciocopyright text .editorconfig text .gitattributes text .gitconfig text .gitignore text .gitmodules text *.lektorproject text .nojekyll text .project text AUTHORS text CHANGELOG text CHANGES text CONTRIBUTING text INSTALL text license text LICENSE text NEWS text NOTICES text readme text *README* text RELEASE text TODO text browserslist text contents.lr text makefile text Makefile text MANIFEST.in text # Declare files that will always have CRLF line endings on checkout. *.bat text eol=crlf *.vbs text eol=crlf *.ps1 text eol=crlf # Denote all files that are truly binary and should not be modified. # Executable *.app binary *.bin binary *.deb binary *.dll binary *.dylib binary *.elf binary *.exe binary *.ko binary *.lib binary *.msi binary *.o binary *.obj binary *.pyc binary *.pyd binary *.pyo binary *.rdb binary *.rdx binary *.Rdx binary *.rpm binary *.so binary *.sys binary # Data *.cdf binary *.db binary *.dta binary *.feather binary *.fit binary *.fits binary *.fts binary *.fods binary *.geotiff binary *.gpkg binary *.h4 binary *.h5 binary *.hdf binary *.hdf4 binary *.hdf5 binary *.mat binary *.nc binary *.npy binary *.npz binary *.odb binary *.ods binary *.p binary *.parquet binary *.pickle binary *.pkl binary *.rds binary *.Rds binary *.sav binary *.sqlite binary *.wkb binary *.xls binary *.XLS binary *.xlsx binary *.XLSX binary # Documents *.doc binary *.DOC binary *.docx binary *.DOCX binary *.epub binary *.fodp binary *.fodt binary *.odp binary *.odt binary *.pdf binary *.PDF binary *.ppt binary *.PPT binary *.pptx binary *.PPTX binary *.rtf binary *.RTF binary # Graphics *.ai binary *.bmp binary *.eps binary *.fodg binary *.gif binary *.icns binary *.ico binary *.jp2 binary *.jpeg binary *.jpg binary *.mo binary *.pdn binary *.png binary *.PNG binary *.psd binary *.odg binary *.svg binary *.svgz binary *.tif binary *.tiff binary *.webp binary *.xcf binary # Fonts *.eot binary *.otc binary *.otf binary *.ttc binary *.ttf binary *.woff binary *.woff2 binary # Audio/Video *.aac binary *.flac binary *.mka binary *.mkv binary *.mp3 binary *.mp4 binary *.oga binary *.ogg binary *.ogv binary *.opus binary *.wav binary *.webm binary # Archives *.7z binary *.bz2 binary *.dmg binary *.gz binary *.lz binary *.lzma binary *.rar binary *.sz binary *.tar binary *.tbz2 binary *.tgz binary *.tlz binary *.txz binary *.xz binary *.zip binary # Spyder-related *.results binary *.spydata binary # Other *.bak binary *.lnk binary *.temp binary *.tmp binary spyder-kernels-3.0.3/.github/000077500000000000000000000000001475072611100160305ustar00rootroot00000000000000spyder-kernels-3.0.3/.github/FUNDING.yml000066400000000000000000000000301475072611100176360ustar00rootroot00000000000000open_collective: spyder spyder-kernels-3.0.3/.github/workflows/000077500000000000000000000000001475072611100200655ustar00rootroot00000000000000spyder-kernels-3.0.3/.github/workflows/linux-pip-tests.yml000066400000000000000000000035601475072611100237010ustar00rootroot00000000000000name: Linux pip tests on: push: branches: - master - 3.* pull_request: branches: - master - 3.* concurrency: group: linux-pip-tests-${{ github.ref }} cancel-in-progress: true jobs: linux: name: Linux (pip) - Py${{ matrix.PYTHON_VERSION }} runs-on: ubuntu-20.04 env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} RUNNER_OS: 'ubuntu' USE_CONDA: 'false' strategy: fail-fast: false matrix: PYTHON_VERSION: ['3.8', '3.9', '3.10'] timeout-minutes: 20 steps: - name: Checkout branch uses: actions/checkout@v1 - name: Install System Packages run: | sudo apt-get update sudo apt-get install libegl1-mesa libopengl0 - name: Install Conda uses: conda-incubator/setup-miniconda@v3 with: activate-environment: test auto-update-conda: false auto-activate-base: false python-version: ${{ matrix.PYTHON_VERSION }} - name: Install package and dependencies shell: bash -l {0} run: | pip install -e .[test] - name: Show environment information shell: bash -l {0} run: | conda info conda list # - name: Setup Remote SSH Connection # uses: mxschmitt/action-tmate@v3 # timeout-minutes: 60 - name: Run tests shell: bash -l {0} run: | xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: fail_ci_if_error: false verbose: true spyder-kernels-3.0.3/.github/workflows/linux-tests.yml000066400000000000000000000041611475072611100231110ustar00rootroot00000000000000name: Linux tests on: push: branches: - master - 3.* pull_request: branches: - master - 3.* concurrency: group: linux-tests-${{ github.ref }} cancel-in-progress: true jobs: linux: name: Linux - Py${{ matrix.PYTHON_VERSION }} runs-on: ubuntu-20.04 env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} RUNNER_OS: 'ubuntu' USE_CONDA: 'true' strategy: fail-fast: false matrix: PYTHON_VERSION: ['3.8', '3.9', '3.10'] timeout-minutes: 20 steps: - name: Checkout branch uses: actions/checkout@v1 - name: Install System Packages run: | sudo apt-get update sudo apt-get install libegl1-mesa libopengl0 - name: Install Conda uses: conda-incubator/setup-miniconda@v3 with: activate-environment: test auto-update-conda: true auto-activate-base: false channels: conda-forge python-version: ${{ matrix.PYTHON_VERSION }} - name: Install package dependencies shell: bash -l {0} run: | conda install --file requirements/posix.txt -y -q - name: Install test dependencies shell: bash -l {0} run: conda install --file requirements/tests.txt -y -q - name: Install Package shell: bash -l {0} run: pip install -e . - name: Show environment information shell: bash -l {0} run: | conda info conda list # - name: Setup Remote SSH Connection # uses: mxschmitt/action-tmate@v3 # timeout-minutes: 60 - name: Run tests shell: bash -l {0} run: | xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: fail_ci_if_error: false verbose: true spyder-kernels-3.0.3/.github/workflows/macos-tests.yml000066400000000000000000000036301475072611100230540ustar00rootroot00000000000000name: Macos tests on: push: branches: - master - 3.* pull_request: branches: - master - 3.* concurrency: group: macos-tests-${{ github.ref }} cancel-in-progress: true jobs: macos: name: macOS - Py${{ matrix.PYTHON_VERSION }} runs-on: macos-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} RUNNER_OS: 'macos' USE_CONDA: 'true' strategy: fail-fast: false matrix: PYTHON_VERSION: ['3.8', '3.9', '3.10'] timeout-minutes: 25 steps: - name: Checkout branch uses: actions/checkout@v1 - name: Install Conda uses: conda-incubator/setup-miniconda@v3 with: activate-environment: test auto-update-conda: true auto-activate-base: false channels: conda-forge python-version: ${{ matrix.PYTHON_VERSION }} - name: Install package dependencies shell: bash -l {0} run: | conda install --file requirements/posix.txt -y -q - name: Install test dependencies shell: bash -l {0} run: conda install --file requirements/tests.txt -y -q - name: Install Package shell: bash -l {0} run: pip install -e . - name: Show environment information shell: bash -l {0} run: | conda info conda list # - name: Setup Remote SSH Connection # uses: mxschmitt/action-tmate@v3 # timeout-minutes: 60 - name: Run tests shell: bash -l {0} run: | pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ pytest spyder_kernels --color=yes --cov=spyder_kernels -vv - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: fail_ci_if_error: false verbose: true spyder-kernels-3.0.3/.github/workflows/windows-tests.yml000066400000000000000000000036321475072611100234460ustar00rootroot00000000000000name: Windows tests on: push: branches: - master - 3.* pull_request: branches: - master - 3.* concurrency: group: windows-tests-${{ github.ref }} cancel-in-progress: true jobs: windows: name: Windows - Py${{ matrix.PYTHON_VERSION }} runs-on: windows-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} RUNNER_OS: 'windows' USE_CONDA: 'true' strategy: fail-fast: false matrix: PYTHON_VERSION: ['3.8', '3.9', '3.10'] timeout-minutes: 25 steps: - name: Checkout branch uses: actions/checkout@v1 - name: Install Conda uses: conda-incubator/setup-miniconda@v3 with: activate-environment: test auto-update-conda: true auto-activate-base: false channels: conda-forge python-version: ${{ matrix.PYTHON_VERSION }} - name: Install package dependencies shell: bash -l {0} run: conda install --file requirements/windows.txt -y -q - name: Install test dependencies shell: bash -l {0} run: | conda install --file requirements/tests.txt -y -q - name: Install Package shell: bash -l {0} run: pip install -e . - name: Show environment information shell: bash -l {0} run: | conda info conda list # - name: Setup Remote SSH Connection # uses: mxschmitt/action-tmate@v3 # timeout-minutes: 60 - name: Run tests shell: bash -l {0} run: | pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \ pytest spyder_kernels --cov=spyder_kernels -vv - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: fail_ci_if_error: false verbose: true spyder-kernels-3.0.3/.gitignore000066400000000000000000000005531475072611100164630ustar00rootroot00000000000000# Compiled python files *.py[ocd] # gedit files *~ # Notepad++ files nppBackup/ # Pytest dirs/files .pytest_cache/ # git .orig files *.orig # Sphinx doc build dir _build/ # Pickle results from tests *.pickle # Special dirs and files build/ dist/ bin/ *.egg-info/ Thumbs.db .DS_Store/ spyder_crash.log .DS_Store .spyproject .idea/ .cache *.tmp *.temp *.bak spyder-kernels-3.0.3/AUTHORS.txt000066400000000000000000000015731475072611100163640ustar00rootroot00000000000000The Spyder Kernels Contributors are composed of: * Pierre Raybaut (Original Spyder author) * Carlos Cordoba (Current Spyder/-Kernels maintainer) * All other developers that have committed to the spyder-kernels repository: and contributors listed on the original files in the main spyder repository: spyder-kernels-3.0.3/CHANGELOG.md000066400000000000000000002277651475072611100163240ustar00rootroot00000000000000# History of changes ## Version 3.0.3 (2025-02-05) ### Pull Requests Merged * [PR 530](https://github.com/spyder-ide/spyder-kernels/pull/530) - PR: Correctly process Pdb commands available in `cmdqueue`, by [@ccordoba12](https://github.com/ccordoba12) * [PR 527](https://github.com/spyder-ide/spyder-kernels/pull/527) - PR: Use an older Ubuntu version on CIs, by [@ccordoba12](https://github.com/ccordoba12) * [PR 526](https://github.com/spyder-ide/spyder-kernels/pull/526) - PR: Filter frames that come from Spyder-kernels in tracebacks and fix tracebacks in Python 3.8, by [@ccordoba12](https://github.com/ccordoba12) In this release 3 pull requests were closed. ---- ## Version 3.0.2 (2024-12-04) ### Pull Requests Merged * [PR 524](https://github.com/spyder-ide/spyder-kernels/pull/524) - PR: Improve release instructions for the new backporting workflow, by [@ccordoba12](https://github.com/ccordoba12) * [PR 521](https://github.com/spyder-ide/spyder-kernels/pull/521) - PR: Add logic to handle traceback color configuration, by [@dalthviz](https://github.com/dalthviz) In this release 2 pull requests were closed. ---- ## Version 3.0.1 (2024-10-29) ### Pull Requests Merged * [PR 517](https://github.com/spyder-ide/spyder-kernels/pull/517) - PR: Manually register the Matplotlib inline backend in case it hasn't, by [@ccordoba12](https://github.com/ccordoba12) * [PR 514](https://github.com/spyder-ide/spyder-kernels/pull/514) - PR: Fix development version for the stable branch, by [@ccordoba12](https://github.com/ccordoba12) * [PR 510](https://github.com/spyder-ide/spyder-kernels/pull/510) - PR: Replace Quansight logo by CZI one in Readme, by [@ccordoba12](https://github.com/ccordoba12) * [PR 508](https://github.com/spyder-ide/spyder-kernels/pull/508) - PR: Update workflows to run in the `3.x` branch (CI), by [@ccordoba12](https://github.com/ccordoba12) * [PR 506](https://github.com/spyder-ide/spyder-kernels/pull/506) - PR: Make `glob` and `loc` kwargs of `debug_exec`, by [@impact27](https://github.com/impact27) * [PR 504](https://github.com/spyder-ide/spyder-kernels/pull/504) - PR: Update workflow actions (CI), by [@mrclary](https://github.com/mrclary) * [PR 503](https://github.com/spyder-ide/spyder-kernels/pull/503) - PR: Update `load_dicom` to accommodate Pydicom 3.0, by [@mrclary](https://github.com/mrclary) In this release 7 pull requests were closed. ---- ## Version 3.0.0 (2024-08-29) ### New features * Speed up debugger execution. * Notify Spyder when Matplotlib backend changes. * Use control channel for comms instead of a special one. * Update variable explorer from the kernel. * Simplify kernel configuration from Spyder. * Add a `comm_handler` decorator. * Transform `runfile`, `debugfile` and `runcell` commands to IPython magics. * Add comm handlers to interrupt executions and enter the debugger after that. * Publish Pdb stack frames to Spyder. * Drop support for Python 2 and support Python 3.8+ ### Pull Requests Merged * [PR 500](https://github.com/spyder-ide/spyder-kernels/pull/500) - PR: Restore `TMPDIR` env var if it was available on the Spyder side, by [@ccordoba12](https://github.com/ccordoba12) * [PR 498](https://github.com/spyder-ide/spyder-kernels/pull/498) - PR: Remove `TMPDIR` env var after initialization, by [@ccordoba12](https://github.com/ccordoba12) In this release 2 pull requests were closed. ---- ## Version 3.0.0b9 (2024-08-21) ### Pull Requests Merged * [PR 495](https://github.com/spyder-ide/spyder-kernels/pull/495) - PR: Add comm handler to get information about the Python environment associated to the kernel, by [@ccordoba12](https://github.com/ccordoba12) In this release 1 pull request was closed. ---- ## Version 3.0.0b8 (2024-08-08) ### Pull Requests Merged * [PR 496](https://github.com/spyder-ide/spyder-kernels/pull/496) - PR: Add filtering logic for stream data and constraint `setuptools` version, by [@dalthviz](https://github.com/dalthviz) In this release 1 pull request was closed. ---- ## Version 3.0.0b7 (2024-06-18) ### Issues Closed * [Issue 491](https://github.com/spyder-ide/spyder-kernels/issues/491) - Debugger misses breakpoint when a file can have several canonic paths ([PR 490](https://github.com/spyder-ide/spyder-kernels/pull/490) by [@impact27](https://github.com/impact27)) * [Issue 468](https://github.com/spyder-ide/spyder-kernels/issues/468) - Debugger not working for environments with different Python versions ([PR 492](https://github.com/spyder-ide/spyder-kernels/pull/492) by [@impact27](https://github.com/impact27)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 492](https://github.com/spyder-ide/spyder-kernels/pull/492) - PR: Enable comms to work across different Python versions, by [@impact27](https://github.com/impact27) ([468](https://github.com/spyder-ide/spyder-kernels/issues/468)) * [PR 490](https://github.com/spyder-ide/spyder-kernels/pull/490) - PR: Use inodes for single canonic file path (Debugger), by [@impact27](https://github.com/impact27) ([491](https://github.com/spyder-ide/spyder-kernels/issues/491)) * [PR 487](https://github.com/spyder-ide/spyder-kernels/pull/487) - PR: Fix issue where Spyder's inline graphics preferences were not applied, by [@mrclary](https://github.com/mrclary) In this release 3 pull requests were closed. ---- ## Version 3.0.0b6 (2024-05-15) ### Issues Closed * [Issue 457](https://github.com/spyder-ide/spyder-kernels/issues/457) - Detecting the interactive backend started to fail on Mac in master ([PR 486](https://github.com/spyder-ide/spyder-kernels/pull/486) by [@ccordoba12](https://github.com/ccordoba12)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 486](https://github.com/spyder-ide/spyder-kernels/pull/486) - PR: Run `test_get_interactive_backend` again on Mac, by [@ccordoba12](https://github.com/ccordoba12) ([457](https://github.com/spyder-ide/spyder-kernels/issues/457)) * [PR 485](https://github.com/spyder-ide/spyder-kernels/pull/485) - PR: Fix Matplotlib interactive backend detection, by [@ccordoba12](https://github.com/ccordoba12) In this release 2 pull requests were closed. ---- ## Version 3.0.0b5 (2024-04-23) ### Pull Requests Merged * [PR 481](https://github.com/spyder-ide/spyder-kernels/pull/481) - PR: Allow magic to edit locals while debugging, by [@impact27](https://github.com/impact27) * [PR 480](https://github.com/spyder-ide/spyder-kernels/pull/480) - PR: Save faulthandler files under `xdg_data_home/spyder` on Linux, by [@ccordoba12](https://github.com/ccordoba12) In this release 2 pull requests were closed. ---- ## Version 3.0.0b4 (2024-02-08) ### Pull Requests Merged * [PR 477](https://github.com/spyder-ide/spyder-kernels/pull/477) - PR: Handle new Inline backend options `fontsize` and `bottom`, by [@jitseniesen](https://github.com/jitseniesen) In this release 1 pull request was closed. ---- ## Version 3.0.0b3 (2023-12-18) ### Pull Requests Merged * [PR 476](https://github.com/spyder-ide/spyder-kernels/pull/476) - PR: Send back pickling error correctly, by [@impact27](https://github.com/impact27) * [PR 467](https://github.com/spyder-ide/spyder-kernels/pull/467) - PR: Fix index when skipping hidden frames (Debugger), by [@impact27](https://github.com/impact27) * [PR 466](https://github.com/spyder-ide/spyder-kernels/pull/466) - PR: Simplify kernel configuration, by [@impact27](https://github.com/impact27) In this release 3 pull requests were closed. ---- ## Version 3.0.0b2 (2023-08-22) ### Pull Requests Merged * [PR 465](https://github.com/spyder-ide/spyder-kernels/pull/465) - Save temporary file in test to temporary location, by [@juliangilbey](https://github.com/juliangilbey) * [PR 464](https://github.com/spyder-ide/spyder-kernels/pull/464) - Remove locals inspection, by [@impact27](https://github.com/impact27) * [PR 460](https://github.com/spyder-ide/spyder-kernels/pull/460) - PR: Add a global filter flag to settings, by [@jsbautista](https://github.com/jsbautista) * [PR 445](https://github.com/spyder-ide/spyder-kernels/pull/445) - PR: Add `exitdb` command and some speed optimizations to the debugger, by [@impact27](https://github.com/impact27) * [PR 429](https://github.com/spyder-ide/spyder-kernels/pull/429) - PR: Add a comm handler decorator, by [@impact27](https://github.com/impact27) * [PR 411](https://github.com/spyder-ide/spyder-kernels/pull/411) - PR: Remove `set_debug_state` and `do_where` calls, by [@impact27](https://github.com/impact27) In this release 6 pull requests were closed. ---- ## Version 3.0.0b1 (2023-06-14) ### Issues Closed * [Issue 425](https://github.com/spyder-ide/spyder-kernels/issues/425) - Possible minor issues related to post mortem debugging ([PR 444](https://github.com/spyder-ide/spyder-kernels/pull/444) by [@impact27](https://github.com/impact27)) * [Issue 340](https://github.com/spyder-ide/spyder-kernels/issues/340) - Drop support for Python 2 ([PR 341](https://github.com/spyder-ide/spyder-kernels/pull/341) by [@impact27](https://github.com/impact27)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 456](https://github.com/spyder-ide/spyder-kernels/pull/456) - PR: Remove unnecessary code for old IPykernel versions in our tests, by [@ccordoba12](https://github.com/ccordoba12) * [PR 453](https://github.com/spyder-ide/spyder-kernels/pull/453) - PR: Move code that loads and saves HDF5 and DICOM files from Spyder, by [@ccordoba12](https://github.com/ccordoba12) * [PR 447](https://github.com/spyder-ide/spyder-kernels/pull/447) - PR: Create magics for run|debug file|cell, by [@impact27](https://github.com/impact27) * [PR 446](https://github.com/spyder-ide/spyder-kernels/pull/446) - PR: Make call to interrupt children processes work for IPykernel greater than 6.21.2, by [@ccordoba12](https://github.com/ccordoba12) * [PR 444](https://github.com/spyder-ide/spyder-kernels/pull/444) - PR: Fix post mortem for debugging, by [@impact27](https://github.com/impact27) ([425](https://github.com/spyder-ide/spyder-kernels/issues/425)) * [PR 443](https://github.com/spyder-ide/spyder-kernels/pull/443) - PR: Fix small typo in Readme, by [@davidbrochart](https://github.com/davidbrochart) * [PR 437](https://github.com/spyder-ide/spyder-kernels/pull/437) - PR: Remove imports from __future__, by [@oscargus](https://github.com/oscargus) * [PR 421](https://github.com/spyder-ide/spyder-kernels/pull/421) - PR: Update variable explorer from the kernel, by [@impact27](https://github.com/impact27) * [PR 417](https://github.com/spyder-ide/spyder-kernels/pull/417) - PR: Fix error in `globalsfilter`, by [@ccordoba12](https://github.com/ccordoba12) * [PR 412](https://github.com/spyder-ide/spyder-kernels/pull/412) - PR: Use temporary file for faulthandler and make sure iopub is open for comms, by [@impact27](https://github.com/impact27) * [PR 409](https://github.com/spyder-ide/spyder-kernels/pull/409) - PR: Add Python executable as part of kernel info, by [@impact27](https://github.com/impact27) * [PR 408](https://github.com/spyder-ide/spyder-kernels/pull/408) - PR: Expose package version in SpyderShell, by [@impact27](https://github.com/impact27) * [PR 403](https://github.com/spyder-ide/spyder-kernels/pull/403) - PR: Make debugger faster by avoiding unnecessary comm messages, by [@impact27](https://github.com/impact27) * [PR 401](https://github.com/spyder-ide/spyder-kernels/pull/401) - PR: Wait for connection file to be written, by [@impact27](https://github.com/impact27) * [PR 400](https://github.com/spyder-ide/spyder-kernels/pull/400) - PR: Use `execute_interactive` to print errors during tests, by [@impact27](https://github.com/impact27) * [PR 397](https://github.com/spyder-ide/spyder-kernels/pull/397) - PR: Remove Python 2 code introduced when merging PR #395, by [@impact27](https://github.com/impact27) * [PR 396](https://github.com/spyder-ide/spyder-kernels/pull/396) - PR: Add handlers to interrupt executions and enter the debugger after that, by [@impact27](https://github.com/impact27) * [PR 390](https://github.com/spyder-ide/spyder-kernels/pull/390) - PR: Filter comm socket thread, by [@impact27](https://github.com/impact27) * [PR 387](https://github.com/spyder-ide/spyder-kernels/pull/387) - PR: Minor changes to finish the migration to Python 3, by [@ccordoba12](https://github.com/ccordoba12) * [PR 366](https://github.com/spyder-ide/spyder-kernels/pull/366) - PR: Print warning when using `global` in an empty namespace, by [@impact27](https://github.com/impact27) * [PR 341](https://github.com/spyder-ide/spyder-kernels/pull/341) - PR: Drop support for Python 2, by [@impact27](https://github.com/impact27) ([340](https://github.com/spyder-ide/spyder-kernels/issues/340)) * [PR 339](https://github.com/spyder-ide/spyder-kernels/pull/339) - PR: Use control channel for comms, by [@impact27](https://github.com/impact27) * [PR 286](https://github.com/spyder-ide/spyder-kernels/pull/286) - PR: Improve and refactor the way we run and debug code, by [@impact27](https://github.com/impact27) * [PR 257](https://github.com/spyder-ide/spyder-kernels/pull/257) - PR: Notify frontend of Matplotlib backend change, by [@impact27](https://github.com/impact27) * [PR 171](https://github.com/spyder-ide/spyder-kernels/pull/171) - PR: Publish Pdb stack frames to Spyder, by [@impact27](https://github.com/impact27) In this release 25 pull requests were closed. ---- ## Version 2.5.2 (2024-06-11) ### Pull Requests Merged * [PR 489](https://github.com/spyder-ide/spyder-kernels/pull/489) - PR: Fix detecting Matplotlib backend for its 3.9.0 version, by [@mrclary](https://github.com/mrclary) In this release 1 pull request was closed. ---- ## Version 2.5.1 (2024-02-28) ### Pull Requests Merged * [PR 479](https://github.com/spyder-ide/spyder-kernels/pull/479) - PR: Fix hangs with Maplotlib interactive backends, by [@ccordoba12](https://github.com/ccordoba12) In this release 1 pull request was closed. ---- ## Version 2.5.0 (2023-11-06) ### New features * Add support for chained exceptions to the debugger. * Improve getting signatures from docstrings. * Restore compatibility with Python 2. ### Pull Requests Merged * [PR 475](https://github.com/spyder-ide/spyder-kernels/pull/475) - PR: Skip IPython 8.17.1 in our dependencies for Python 3.9+, by [@ccordoba12](https://github.com/ccordoba12) * [PR 474](https://github.com/spyder-ide/spyder-kernels/pull/474) - PR: More improvements to getting signatures from text, by [@ccordoba12](https://github.com/ccordoba12) * [PR 473](https://github.com/spyder-ide/spyder-kernels/pull/473) - PR: Improve getting signatures from docstrings and catch error when trying to get the signature of some objects, by [@ccordoba12](https://github.com/ccordoba12) * [PR 472](https://github.com/spyder-ide/spyder-kernels/pull/472) - PR: Add support for chained exceptions to the debugger, by [@ccordoba12](https://github.com/ccordoba12) * [PR 471](https://github.com/spyder-ide/spyder-kernels/pull/471) - PR: Improve the way we depend on IPython and IPykernel per Python version, by [@ccordoba12](https://github.com/ccordoba12) * [PR 469](https://github.com/spyder-ide/spyder-kernels/pull/469) - PR: Restore compatibility with Python 2, by [@ccordoba12](https://github.com/ccordoba12) In this release 6 pull requests were closed. ---- ## Version 2.4.4 (2023-06-29) ### Issues Closed * [Issue 454](https://github.com/spyder-ide/spyder-kernels/issues/454) - Codecov Package was Yanked ([PR 455](https://github.com/spyder-ide/spyder-kernels/pull/455) by [@ccordoba12](https://github.com/ccordoba12)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 461](https://github.com/spyder-ide/spyder-kernels/pull/461) - PR: Increase minimal required version of IPykernel to 6.23.2, by [@ccordoba12](https://github.com/ccordoba12) * [PR 459](https://github.com/spyder-ide/spyder-kernels/pull/459) - PR: Skip more buggy IPython versions, by [@ccordoba12](https://github.com/ccordoba12) * [PR 458](https://github.com/spyder-ide/spyder-kernels/pull/458) - PR: Disable IPython's debugger skip functionality by default, by [@ccordoba12](https://github.com/ccordoba12) * [PR 455](https://github.com/spyder-ide/spyder-kernels/pull/455) - PR: Remove `codecov` package and use Github action to upload coverage instead, by [@ccordoba12](https://github.com/ccordoba12) ([454](https://github.com/spyder-ide/spyder-kernels/issues/454)) In this release 4 pull requests were closed. ---- ## Version 2.4.3 (2023-04-02) ### Issues Closed * [Issue 440](https://github.com/spyder-ide/spyder-kernels/issues/440) - distutils and LooseVersion deprecation ([PR 450](https://github.com/spyder-ide/spyder-kernels/pull/450) by [@ccordoba12](https://github.com/ccordoba12)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 452](https://github.com/spyder-ide/spyder-kernels/pull/452) - PR: Fix error when executing empty Python script, by [@rear1019](https://github.com/rear1019) * [PR 450](https://github.com/spyder-ide/spyder-kernels/pull/450) - PR: Remove usage of `distutils.LooseVersion`, by [@ccordoba12](https://github.com/ccordoba12) ([440](https://github.com/spyder-ide/spyder-kernels/issues/440)) * [PR 449](https://github.com/spyder-ide/spyder-kernels/pull/449) - PR: Add support for Jupyter-client 8, by [@ccordoba12](https://github.com/ccordoba12) * [PR 448](https://github.com/spyder-ide/spyder-kernels/pull/448) - PR: Skip IPython versions that give buggy code completions, by [@ccordoba12](https://github.com/ccordoba12) * [PR 442](https://github.com/spyder-ide/spyder-kernels/pull/442) - PR: Add FreeBSD to `test_user_sitepackages_in_pathlist`, by [@rhurlin](https://github.com/rhurlin) * [PR 434](https://github.com/spyder-ide/spyder-kernels/pull/434) - PR: Use `allow_pickle=True` when loading Numpy arrays, by [@nkleinbaer](https://github.com/nkleinbaer) * [PR 430](https://github.com/spyder-ide/spyder-kernels/pull/430) - PR: Inform GUI about position of exception in post mortem debugging, by [@rear1019](https://github.com/rear1019) In this release 7 pull requests were closed. ---- ## Version 2.4.2 (2023-01-17) ### Issues Closed * [Issue 307](https://github.com/spyder-ide/spyder-kernels/issues/307) - formatargspec is deprecated since Python 3.5 ([PR 435](https://github.com/spyder-ide/spyder-kernels/pull/435) by [@juliangilbey](https://github.com/juliangilbey)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 438](https://github.com/spyder-ide/spyder-kernels/pull/438) - PR: Increase minimal required Jupyter-client version to 7.4.9, by [@ccordoba12](https://github.com/ccordoba12) * [PR 435](https://github.com/spyder-ide/spyder-kernels/pull/435) - PR: Replace deprecated `inspect.formatargspec` call, by [@juliangilbey](https://github.com/juliangilbey) ([307](https://github.com/spyder-ide/spyder-kernels/issues/307)) In this release 2 pull requests were closed. ---- ## Version 2.4.1 (2022-12-29) ### Issues Closed * [Issue 427](https://github.com/spyder-ide/spyder-kernels/issues/427) - Make spyder-kernels compatible with IPython 8 ([PR 419](https://github.com/spyder-ide/spyder-kernels/pull/419) by [@eendebakpt](https://github.com/eendebakpt)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 419](https://github.com/spyder-ide/spyder-kernels/pull/419) - PR: Update IPython requirement to support version 8, by [@eendebakpt](https://github.com/eendebakpt) ([427](https://github.com/spyder-ide/spyder-kernels/issues/427)) In this release 1 pull request was closed. ---- ## Version 2.4.0 (2022-11-02) ### New features * Add support for Python 3.11 * Flush standard streams after execution. ### Pull Requests Merged * [PR 428](https://github.com/spyder-ide/spyder-kernels/pull/428) - PR: Patch for CVE-2007-4559 Tar directory traversal, by [@ccordoba12](https://github.com/ccordoba12) * [PR 426](https://github.com/spyder-ide/spyder-kernels/pull/426) - PR: Update ipykernel constraint to >=6.16.1, by [@dalthviz](https://github.com/dalthviz) * [PR 423](https://github.com/spyder-ide/spyder-kernels/pull/423) - PR: Fix lineno range, by [@impact27](https://github.com/impact27) ([19862](https://github.com/spyder-ide/spyder/issues/19862)) * [PR 422](https://github.com/spyder-ide/spyder-kernels/pull/422) - PR: Make comm lock reentrant, by [@impact27](https://github.com/impact27) * [PR 420](https://github.com/spyder-ide/spyder-kernels/pull/420) - PR: Only access sys.stdout and sys.stderr once, by [@impact27](https://github.com/impact27) * [PR 416](https://github.com/spyder-ide/spyder-kernels/pull/416) - PR: Fix some errors when computing the namespace view, by [@ccordoba12](https://github.com/ccordoba12) * [PR 415](https://github.com/spyder-ide/spyder-kernels/pull/415) - PR: Fix tk eventloop handling when debugging on Windows, by [@dalthviz](https://github.com/dalthviz) * [PR 413](https://github.com/spyder-ide/spyder-kernels/pull/413) - PR: Flush standard streams after execution, by [@impact27](https://github.com/impact27) In this release 8 pull requests were closed. ---- ## Version 2.3.3 (2022-08-28) ### Issues Closed * [Issue 405](https://github.com/spyder-ide/spyder-kernels/issues/405) - Python 2 tests are broken in Conda slots ([PR 404](https://github.com/spyder-ide/spyder-kernels/pull/404) by [@impact27](https://github.com/impact27)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 407](https://github.com/spyder-ide/spyder-kernels/pull/407) - PR: Use `get_size` to get variable length for `get_var_properties` to prevent triggering Dask tasks, by [@dalthviz](https://github.com/dalthviz) * [PR 404](https://github.com/spyder-ide/spyder-kernels/pull/404) - PR: Use debugging namespace when curframe is active, by [@impact27](https://github.com/impact27) ([405](https://github.com/spyder-ide/spyder-kernels/issues/405)) In this release 2 pull requests were closed. ---- ## Version 2.3.2 (2022-07-06) ### Issues Closed * [Issue 394](https://github.com/spyder-ide/spyder-kernels/issues/394) - The variable explorer is broken while debugging ([PR 395](https://github.com/spyder-ide/spyder-kernels/pull/395) by [@impact27](https://github.com/impact27)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 399](https://github.com/spyder-ide/spyder-kernels/pull/399) - PR: Increase minimal required version of jupyter_client to 7.3.4, by [@ccordoba12](https://github.com/ccordoba12) * [PR 398](https://github.com/spyder-ide/spyder-kernels/pull/398) - PR: Fix module namespace, by [@impact27](https://github.com/impact27) * [PR 395](https://github.com/spyder-ide/spyder-kernels/pull/395) - PR: Fix running namespace and improve eventloop integration while debugging, by [@impact27](https://github.com/impact27) ([394](https://github.com/spyder-ide/spyder-kernels/issues/394)) * [PR 389](https://github.com/spyder-ide/spyder-kernels/pull/389) - PR: Fix debug filename path for remote debugging, by [@impact27](https://github.com/impact27) ([18330](https://github.com/spyder-ide/spyder/issues/18330)) * [PR 388](https://github.com/spyder-ide/spyder-kernels/pull/388) - PR: Fix getting args from functions or methods, by [@ccordoba12](https://github.com/ccordoba12) * [PR 386](https://github.com/spyder-ide/spyder-kernels/pull/386) - PR: Fix flaky test, by [@impact27](https://github.com/impact27) * [PR 381](https://github.com/spyder-ide/spyder-kernels/pull/381) - PR: Eliminate unnecessary updates of the Variable Explorer while debugging, by [@rear1019](https://github.com/rear1019) * [PR 378](https://github.com/spyder-ide/spyder-kernels/pull/378) - PR: Append paths that come from Spyder's Python path manager to the end of `sys.path`, by [@mrclary](https://github.com/mrclary) In this release 8 pull requests were closed. ---- ## Version 2.3.1 (2022-05-21) ### Pull Requests Merged * [PR 385](https://github.com/spyder-ide/spyder-kernels/pull/385) - PR: Add an upper constraint to our most important dependencies, by [@ccordoba12](https://github.com/ccordoba12) * [PR 384](https://github.com/spyder-ide/spyder-kernels/pull/384) - PR: Prevent an error in Python 2 with our tests, by [@ccordoba12](https://github.com/ccordoba12) * [PR 382](https://github.com/spyder-ide/spyder-kernels/pull/382) - PR: Increase minimal required version of jupyter_client to 7.3.1, by [@ccordoba12](https://github.com/ccordoba12) In this release 3 pull requests were closed. --- ## Version 2.3.0 (2022-03-30) ### New features * Add new handler to detect the current Matplotlib interactive backend. * Print last line of cells and Pdb code. ### Issues Closed * [Issue 371](https://github.com/spyder-ide/spyder-kernels/issues/371) - Can't debug comprehensions ([PR 370](https://github.com/spyder-ide/spyder-kernels/pull/370) by [@impact27](https://github.com/impact27)) * [Issue 344](https://github.com/spyder-ide/spyder-kernels/issues/344) - Drop support for Python 3.6 by requiring a more recent IPython version ([PR 373](https://github.com/spyder-ide/spyder-kernels/pull/373) by [@ccordoba12](https://github.com/ccordoba12)) * [Issue 330](https://github.com/spyder-ide/spyder-kernels/issues/330) - spydercustomize in debug stack ([PR 355](https://github.com/spyder-ide/spyder-kernels/pull/355) by [@impact27](https://github.com/impact27)) * [Issue 320](https://github.com/spyder-ide/spyder-kernels/issues/320) - The length of the "excluded_names" list increases. ([PR 372](https://github.com/spyder-ide/spyder-kernels/pull/372) by [@impact27](https://github.com/impact27)) In this release 4 issues were closed. ### Pull Requests Merged * [PR 379](https://github.com/spyder-ide/spyder-kernels/pull/379) - PR: Increase minimal required version of ipykernel to 6.9.2, by [@ccordoba12](https://github.com/ccordoba12) * [PR 377](https://github.com/spyder-ide/spyder-kernels/pull/377) - PR: Handle getting dasks objects size, by [@dalthviz](https://github.com/dalthviz) * [PR 376](https://github.com/spyder-ide/spyder-kernels/pull/376) - PR: Add new handler to detect the current Matplotlib interactive backend, by [@ccordoba12](https://github.com/ccordoba12) * [PR 375](https://github.com/spyder-ide/spyder-kernels/pull/375) - PR: Remove support for outdated and unused Matplotlib backends, by [@ccordoba12](https://github.com/ccordoba12) * [PR 374](https://github.com/spyder-ide/spyder-kernels/pull/374) - PR: Catch FileNotFoundError when trying to get file code from the frontend, by [@ccordoba12](https://github.com/ccordoba12) * [PR 373](https://github.com/spyder-ide/spyder-kernels/pull/373) - PR: Increase minimal required IPython version to 7.31.1, by [@ccordoba12](https://github.com/ccordoba12) ([344](https://github.com/spyder-ide/spyder-kernels/issues/344)) * [PR 372](https://github.com/spyder-ide/spyder-kernels/pull/372) - PR: Avoid modifying the namespace view settings by reference, by [@impact27](https://github.com/impact27) ([320](https://github.com/spyder-ide/spyder-kernels/issues/320)) * [PR 370](https://github.com/spyder-ide/spyder-kernels/pull/370) - PR: Fix issues when debugging comprehensions, by [@impact27](https://github.com/impact27) ([371](https://github.com/spyder-ide/spyder-kernels/issues/371)) * [PR 369](https://github.com/spyder-ide/spyder-kernels/pull/369) - PR: Leave unsaved files in linecache, by [@impact27](https://github.com/impact27) * [PR 355](https://github.com/spyder-ide/spyder-kernels/pull/355) - PR: Print last line of Pdb code and some debugger fixes, by [@impact27](https://github.com/impact27) ([330](https://github.com/spyder-ide/spyder-kernels/issues/330)) In this release 10 pull requests were closed. ---- ## Version 2.2.1 (2022-01-13) ### Issues Closed * [Issue 365](https://github.com/spyder-ide/spyder-kernels/issues/365) - `IOStream.flush timed out` message shown after every evaluation ([PR 367](https://github.com/spyder-ide/spyder-kernels/pull/367) by [@ccordoba12](https://github.com/ccordoba12)) * [Issue 347](https://github.com/spyder-ide/spyder-kernels/issues/347) - Issues handling Matplotlib backend settings ([PR 368](https://github.com/spyder-ide/spyder-kernels/pull/368) by [@ccordoba12](https://github.com/ccordoba12)) * [Issue 345](https://github.com/spyder-ide/spyder-kernels/issues/345) - Namespace issues ([PR 346](https://github.com/spyder-ide/spyder-kernels/pull/346) by [@impact27](https://github.com/impact27)) * [Issue 343](https://github.com/spyder-ide/spyder-kernels/issues/343) - Shapely 1.8 and get_namespace_view spyder call raising deprecation warning ([PR 350](https://github.com/spyder-ide/spyder-kernels/pull/350) by [@ccordoba12](https://github.com/ccordoba12)) In this release 4 issues were closed. ### Pull Requests Merged * [PR 368](https://github.com/spyder-ide/spyder-kernels/pull/368) - PR: Improve error message when setting a Matplotlib backend whose module is not present, by [@ccordoba12](https://github.com/ccordoba12) ([347](https://github.com/spyder-ide/spyder-kernels/issues/347)) * [PR 367](https://github.com/spyder-ide/spyder-kernels/pull/367) - PR: Filter another benign message coming from comm handlers, by [@ccordoba12](https://github.com/ccordoba12) ([365](https://github.com/spyder-ide/spyder-kernels/issues/365)) * [PR 364](https://github.com/spyder-ide/spyder-kernels/pull/364) - PR: Catch error when validating if an object is defined in the namespace, by [@ccordoba12](https://github.com/ccordoba12) * [PR 363](https://github.com/spyder-ide/spyder-kernels/pull/363) - PR: Fix error when running multiprocessing code that contains classes, by [@impact27](https://github.com/impact27) * [PR 362](https://github.com/spyder-ide/spyder-kernels/pull/362) - PR: Catch KeyError when closing comm, by [@ccordoba12](https://github.com/ccordoba12) * [PR 361](https://github.com/spyder-ide/spyder-kernels/pull/361) - PR: Increase minimal required version of pyzmq for Python 3, by [@ccordoba12](https://github.com/ccordoba12) * [PR 360](https://github.com/spyder-ide/spyder-kernels/pull/360) - PR: Increase minimal required version of ipykernel to 6.6.1, by [@ccordoba12](https://github.com/ccordoba12) * [PR 357](https://github.com/spyder-ide/spyder-kernels/pull/357) - PR: Require jupyter-client 7.1.0 to fix issues on Windows, by [@ccordoba12](https://github.com/ccordoba12) * [PR 356](https://github.com/spyder-ide/spyder-kernels/pull/356) - PR: Increase minimal required version of jupyter-client to 7.0+ for Python 3, by [@ccordoba12](https://github.com/ccordoba12) * [PR 354](https://github.com/spyder-ide/spyder-kernels/pull/354) - PR: Constraint IPython version to avoid issues when its next major version is released, by [@ccordoba12](https://github.com/ccordoba12) * [PR 353](https://github.com/spyder-ide/spyder-kernels/pull/353) - PR: Implement a less hacky solution to fix namespace issues, by [@impact27](https://github.com/impact27) * [PR 351](https://github.com/spyder-ide/spyder-kernels/pull/351) - PR: More namespace fixes, by [@impact27](https://github.com/impact27) * [PR 350](https://github.com/spyder-ide/spyder-kernels/pull/350) - PR: Don't print DeprecationWarning's that come from comm handlers, by [@ccordoba12](https://github.com/ccordoba12) ([343](https://github.com/spyder-ide/spyder-kernels/issues/343)) * [PR 349](https://github.com/spyder-ide/spyder-kernels/pull/349) - PR: Update Quansight logo in Readme, by [@ccordoba12](https://github.com/ccordoba12) * [PR 346](https://github.com/spyder-ide/spyder-kernels/pull/346) - PR: Fix some namespace issues, by [@impact27](https://github.com/impact27) ([345](https://github.com/spyder-ide/spyder-kernels/issues/345)) * [PR 338](https://github.com/spyder-ide/spyder-kernels/pull/338) - PR: Fix misplaced section in changelog, by [@ccordoba12](https://github.com/ccordoba12) In this release 16 pull requests were closed. ---- ## Version 2.2.0 (2021-11-22) ### New features * Add the ability to capture segfaults and flush standard streams from Spyder. * Add support for jupyter_client 7. ### Issues Closed * [Issue 326](https://github.com/spyder-ide/spyder-kernels/issues/326) - Setting variables in recursive debugging namespaces is broken ([PR 327](https://github.com/spyder-ide/spyder-kernels/pull/327) by [@impact27](https://github.com/impact27)) * [Issue 325](https://github.com/spyder-ide/spyder-kernels/issues/325) - %timeit has issues with the namespace ([PR 327](https://github.com/spyder-ide/spyder-kernels/pull/327) by [@impact27](https://github.com/impact27)) * [Issue 316](https://github.com/spyder-ide/spyder-kernels/issues/316) - Disable capture_fd_output in IPykernel 6+ in favor of Wurlitzer ([PR 337](https://github.com/spyder-ide/spyder-kernels/pull/337) by [@ccordoba12](https://github.com/ccordoba12)) In this release 3 issues were closed. ### Pull Requests Merged * [PR 337](https://github.com/spyder-ide/spyder-kernels/pull/337) - PR: Disable the new mechanism to forward low-level output in IPykernel 6, by [@ccordoba12](https://github.com/ccordoba12) ([316](https://github.com/spyder-ide/spyder-kernels/issues/316)) * [PR 336](https://github.com/spyder-ide/spyder-kernels/pull/336) - PR: Don't show traceback when exiting debugger after breakpoint call, by [@ccordoba12](https://github.com/ccordoba12) * [PR 335](https://github.com/spyder-ide/spyder-kernels/pull/335) - PR: Remove locals from exec in comprehensions (debugger), by [@impact27](https://github.com/impact27) * [PR 334](https://github.com/spyder-ide/spyder-kernels/pull/334) - PR: Fix missing stderr when the kernel crashes, by [@impact27](https://github.com/impact27) * [PR 333](https://github.com/spyder-ide/spyder-kernels/pull/333) - PR: Add cast for shape tuple subclass instances when getting values size, by [@dalthviz](https://github.com/dalthviz) * [PR 332](https://github.com/spyder-ide/spyder-kernels/pull/332) - PR: Remove usage of f_locals.get, by [@impact27](https://github.com/impact27) * [PR 328](https://github.com/spyder-ide/spyder-kernels/pull/328) - PR: Remove upper constraint on jupyter_client, by [@bnavigator](https://github.com/bnavigator) * [PR 327](https://github.com/spyder-ide/spyder-kernels/pull/327) - PR: Fix timeit in Pdb and other namespace issues, by [@impact27](https://github.com/impact27) ([326](https://github.com/spyder-ide/spyder-kernels/issues/326), [325](https://github.com/spyder-ide/spyder-kernels/issues/325)) * [PR 317](https://github.com/spyder-ide/spyder-kernels/pull/317) - PR: Run asyncio and normal handlers, by [@impact27](https://github.com/impact27) ([16183](https://github.com/spyder-ide/spyder/issues/16183)) * [PR 254](https://github.com/spyder-ide/spyder-kernels/pull/254) - PR: Enable faulthandler from the frontend, by [@impact27](https://github.com/impact27) In this release 10 pull requests were closed. ---- ## Version 2.1.3 (2021-10-02) ### Pull Requests Merged * [PR 319](https://github.com/spyder-ide/spyder-kernels/pull/319) - PR: Fix errors when setting or getting the Matplotlib backend, by [@ccordoba12](https://github.com/ccordoba12) In this release 1 pull request was closed. ---- ## Version 2.1.2 (2021-09-28) ### Pull Requests Merged * [PR 323](https://github.com/spyder-ide/spyder-kernels/pull/323) - PR: Add `ipython_genutils` dependency for testing, by [@ccordoba12](https://github.com/ccordoba12) * [PR 322](https://github.com/spyder-ide/spyder-kernels/pull/322) - PR: Prevent other libraries to change the breakpoint builtin, by [@ccordoba12](https://github.com/ccordoba12) In this release 2 pull requests were closed. ---- ## Version 2.1.1 (2021-09-01) ### Pull Requests Merged * [PR 318](https://github.com/spyder-ide/spyder-kernels/pull/318) - PR: Avoid runfile to be shadowed by other packages, by [@ccordoba12](https://github.com/ccordoba12) * [PR 314](https://github.com/spyder-ide/spyder-kernels/pull/314) - PR: Remove dependency on ipython_genutils, by [@Carreau](https://github.com/Carreau) * [PR 313](https://github.com/spyder-ide/spyder-kernels/pull/313) - PR: Memoize results of is_module_installed, by [@ccordoba12](https://github.com/ccordoba12) * [PR 309](https://github.com/spyder-ide/spyder-kernels/pull/309) - PR: Restrict jupyter-client to be less than version 7, by [@ccordoba12](https://github.com/ccordoba12) In this release 4 pull requests were closed. ---- ## Version 2.1.0 (2021-07-31) ### New features * Add support for the Rich and Colorama libraries. * Load big modules (e.g. Numpy, Pandas and Scipy) only until it's really necessary. ### Issues Closed * [Issue 302](https://github.com/spyder-ide/spyder-kernels/issues/302) - Add option to use custom Outstream in kernel app ([PR 305](https://github.com/spyder-ide/spyder-kernels/pull/305) by [@eendebakpt](https://github.com/eendebakpt)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 306](https://github.com/spyder-ide/spyder-kernels/pull/306) - PR: Patch os.get_terminal_size to return a terminal size, by [@ccordoba12](https://github.com/ccordoba12) * [PR 305](https://github.com/spyder-ide/spyder-kernels/pull/305) - PR: Use an outstream with isatty() equal to True, by [@eendebakpt](https://github.com/eendebakpt) ([302](https://github.com/spyder-ide/spyder-kernels/issues/302)) * [PR 303](https://github.com/spyder-ide/spyder-kernels/pull/303) - PR: Remove an unnecessary check when getting values in Pdb, by [@ccordoba12](https://github.com/ccordoba12) * [PR 300](https://github.com/spyder-ide/spyder-kernels/pull/300) - PR: Fix %debug magic, by [@impact27](https://github.com/impact27) * [PR 259](https://github.com/spyder-ide/spyder-kernels/pull/259) - PR: Don't import big modules (Numpy, Pandas) until it's really necessary, by [@ccordoba12](https://github.com/ccordoba12) In this release 5 pull requests were closed. ---- ## Version 2.0.5 (2021-07-03) ### Pull Requests Merged * [PR 292](https://github.com/spyder-ide/spyder-kernels/pull/292) - PR: Add support for ipykernel 6, by [@ccordoba12](https://github.com/ccordoba12) In this release 1 pull request was closed. ---- ## Version 2.0.4 (2021-06-10) ### Issues Closed * [Issue 288](https://github.com/spyder-ide/spyder-kernels/issues/288) - Support decorator >= 5 ([PR 301](https://github.com/spyder-ide/spyder-kernels/pull/301) by [@ccordoba12](https://github.com/ccordoba12)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 301](https://github.com/spyder-ide/spyder-kernels/pull/301) - PR: Remove pin on decorator because the Cython magic is working fine again, by [@ccordoba12](https://github.com/ccordoba12) ([288](https://github.com/spyder-ide/spyder-kernels/issues/288)) * [PR 299](https://github.com/spyder-ide/spyder-kernels/pull/299) - PR: Clear argv before adding the exec lines that come from Spyder, by [@ccordoba12](https://github.com/ccordoba12) * [PR 298](https://github.com/spyder-ide/spyder-kernels/pull/298) - PR: Use backslash instead of slash for UMR path regex detection, by [@dalthviz](https://github.com/dalthviz) * [PR 297](https://github.com/spyder-ide/spyder-kernels/pull/297) - PR: Don't report skipped frames for IPython 7.24+ (Debugger), by [@ccordoba12](https://github.com/ccordoba12) * [PR 296](https://github.com/spyder-ide/spyder-kernels/pull/296) - PR: Redefine comm_manager method to avoid showing warning, by [@ccordoba12](https://github.com/ccordoba12) * [PR 295](https://github.com/spyder-ide/spyder-kernels/pull/295) - PR: Fix recursive debugger, by [@impact27](https://github.com/impact27) * [PR 293](https://github.com/spyder-ide/spyder-kernels/pull/293) - PR: Install click 7 to run our tests in Python 2, by [@ccordoba12](https://github.com/ccordoba12) In this release 7 pull requests were closed. ---- ## Version 2.0.3 (2021-05-15) ### Pull Requests Merged * [PR 291](https://github.com/spyder-ide/spyder-kernels/pull/291) - PR: Pass adding breakpoint on ValueError, by [@impact27](https://github.com/impact27) In this release 1 pull request was closed. ---- ## Version 2.0.2 (2021-05-02) ### Pull Requests Merged * [PR 289](https://github.com/spyder-ide/spyder-kernels/pull/289) - PR: Fix setting tight layout in inline figures, by [@ccordoba12](https://github.com/ccordoba12) * [PR 287](https://github.com/spyder-ide/spyder-kernels/pull/287) - PR: Close comm on shutdown, by [@impact27](https://github.com/impact27) * [PR 282](https://github.com/spyder-ide/spyder-kernels/pull/282) - PR: Fix numpy.complex deprecation warning in tests, by [@ArchangeGabriel](https://github.com/ArchangeGabriel) In this release 3 pull requests were closed. ---- ## Version 2.0.1 (2021-04-02) * This release also contains all fixes present in version 1.10.3 ---- ## Version 2.0.0 (2021-04-01) ### New features * Color handling in namespace view was moved to Spyder. ### Pull Requests Merged * [PR 284](https://github.com/spyder-ide/spyder-kernels/pull/284) - PR: Remove handling of colors for object types, by [@ccordoba12](https://github.com/ccordoba12) * [PR 279](https://github.com/spyder-ide/spyder-kernels/pull/279) - PR: Add Python types to namespace view, by [@ccordoba12](https://github.com/ccordoba12) In this release 2 pull requests were closed. ---- ## Version 1.10.3 (2021-04-02) ### Pull Requests Merged * [PR 285](https://github.com/spyder-ide/spyder-kernels/pull/285) - PR: Add a new dependency on decorator to fix the Cython magic, by [@ccordoba12](https://github.com/ccordoba12) In this release 1 pull request was closed. ---- ## Version 1.10.2 (2021-02-21) ### Pull Requests Merged * [PR 278](https://github.com/spyder-ide/spyder-kernels/pull/278) - PR: Warn when comm call creates text output, by [@impact27](https://github.com/impact27) * [PR 277](https://github.com/spyder-ide/spyder-kernels/pull/277) - PR: Increase minimal required version of ipykernel, by [@ccordoba12](https://github.com/ccordoba12) * [PR 275](https://github.com/spyder-ide/spyder-kernels/pull/275) - PR: Better test for non-interactive context, by [@juliangilbey](https://github.com/juliangilbey) * [PR 272](https://github.com/spyder-ide/spyder-kernels/pull/272) - PR: Fix assignment detection in exclamation mark mode (Pdb), by [@impact27](https://github.com/impact27) * [PR 271](https://github.com/spyder-ide/spyder-kernels/pull/271) - PR: Fix recursive debugger, by [@impact27](https://github.com/impact27) In this release 5 pull requests were closed. ---- ## Version 1.10.1 (2020-12-18) ### Issues Closed * [Issue 269](https://github.com/spyder-ide/spyder-kernels/issues/269) - Mark Windows intaller pkgs path as a library to prevent reloading modules ([PR 270](https://github.com/spyder-ide/spyder-kernels/pull/270) by [@dalthviz](https://github.com/dalthviz)) * [Issue 256](https://github.com/spyder-ide/spyder-kernels/issues/256) - ImportError: cannot import name 'leading_empty_lines' ([PR 264](https://github.com/spyder-ide/spyder-kernels/pull/264) by [@ccordoba12](https://github.com/ccordoba12)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 270](https://github.com/spyder-ide/spyder-kernels/pull/270) - PR: Treat 'pkgs' path as a library, by [@dalthviz](https://github.com/dalthviz) ([269](https://github.com/spyder-ide/spyder-kernels/issues/269)) * [PR 267](https://github.com/spyder-ide/spyder-kernels/pull/267) - PR: Remove handlers that require direct code execution, by [@ccordoba12](https://github.com/ccordoba12) * [PR 266](https://github.com/spyder-ide/spyder-kernels/pull/266) - PR: Add exception for quit and exit in SpyderPdb, by [@impact27](https://github.com/impact27) * [PR 265](https://github.com/spyder-ide/spyder-kernels/pull/265) - PR: Add space after dot in a message, by [@impact27](https://github.com/impact27) * [PR 264](https://github.com/spyder-ide/spyder-kernels/pull/264) - PR: Add explicit requirement on IPython and require version 7.6.0+, by [@ccordoba12](https://github.com/ccordoba12) ([256](https://github.com/spyder-ide/spyder-kernels/issues/256)) * [PR 263](https://github.com/spyder-ide/spyder-kernels/pull/263) - PR: Add extra validation to is_module_installed, by [@ccordoba12](https://github.com/ccordoba12) * [PR 262](https://github.com/spyder-ide/spyder-kernels/pull/262) - PR: Avoid infinite hang when quitting external kernels, by [@impact27](https://github.com/impact27) * [PR 261](https://github.com/spyder-ide/spyder-kernels/pull/261) - PR: Fix get_cwd permission errors, by [@steff456](https://github.com/steff456) * [PR 260](https://github.com/spyder-ide/spyder-kernels/pull/260) - PR: Fix Github actions, by [@ccordoba12](https://github.com/ccordoba12) In this release 9 pull requests were closed. ---- ## Version 1.10.0 (2020-11-08) ### New features * Use '!' to prefix Pdb commands. * Show length of all iterables. ### Issues Closed * [Issue 247](https://github.com/spyder-ide/spyder-kernels/issues/247) - Remove from warning that future versions of Spyder will give an error when using non-Python syntax ([PR 243](https://github.com/spyder-ide/spyder-kernels/pull/243) by [@impact27](https://github.com/impact27)) * [Issue 245](https://github.com/spyder-ide/spyder-kernels/issues/245) - Display size of string in variable editor In this release 2 issues were closed. ### Pull Requests Merged * [PR 255](https://github.com/spyder-ide/spyder-kernels/pull/255) - PR: Fix warning for '!' Pdb option, by [@impact27](https://github.com/impact27) * [PR 253](https://github.com/spyder-ide/spyder-kernels/pull/253) - PR: Extend sys.path with paths that come from Spyder, by [@ccordoba12](https://github.com/ccordoba12) * [PR 252](https://github.com/spyder-ide/spyder-kernels/pull/252) - PR: Use frame locals when evaluating code in Pdb, by [@impact27](https://github.com/impact27) * [PR 251](https://github.com/spyder-ide/spyder-kernels/pull/251) - PR: Catch errors when trying to get the len of an object, by [@impact27](https://github.com/impact27) * [PR 250](https://github.com/spyder-ide/spyder-kernels/pull/250) - PR: Show size of objects correctly, if they implement len(), by [@skjerns](https://github.com/skjerns) * [PR 246](https://github.com/spyder-ide/spyder-kernels/pull/246) - PR: Allow sorting with custom sorting key, by [@skjerns](https://github.com/skjerns) * [PR 244](https://github.com/spyder-ide/spyder-kernels/pull/244) - PR: Set multiprocessing ORIGINAL_DIR at startup, by [@impact27](https://github.com/impact27) ([13632](https://github.com/spyder-ide/spyder/issues/13632)) * [PR 243](https://github.com/spyder-ide/spyder-kernels/pull/243) - PR: Allow leading indents in cells, by [@impact27](https://github.com/impact27) ([247](https://github.com/spyder-ide/spyder-kernels/issues/247)) * [PR 242](https://github.com/spyder-ide/spyder-kernels/pull/242) - PR: Display error if required, by [@impact27](https://github.com/impact27) * [PR 240](https://github.com/spyder-ide/spyder-kernels/pull/240) - PR: Hide runcell and runfile frames using a new IPython feature, by [@impact27](https://github.com/impact27) * [PR 239](https://github.com/spyder-ide/spyder-kernels/pull/239) - PR: Improve Pdb input handling, by [@impact27](https://github.com/impact27) * [PR 232](https://github.com/spyder-ide/spyder-kernels/pull/232) - PR: Make do_where go to the current file, by [@impact27](https://github.com/impact27) * [PR 223](https://github.com/spyder-ide/spyder-kernels/pull/223) - PR: Add methods to handle IPython console config on the fly, by [@dalthviz](https://github.com/dalthviz) * [PR 214](https://github.com/spyder-ide/spyder-kernels/pull/214) - PR: Use '!' for Pdb commands, by [@impact27](https://github.com/impact27) In this release 14 pull requests were closed. ---- ## Version 1.9.4 (2020-09-01) ### Pull Requests Merged * [PR 238](https://github.com/spyder-ide/spyder-kernels/pull/238) - PR: Set get_local_scope correctly while debugging, by [@impact27](https://github.com/impact27) * [PR 235](https://github.com/spyder-ide/spyder-kernels/pull/235) - PR: Set debug state correctly on exception, by [@impact27](https://github.com/impact27) * [PR 233](https://github.com/spyder-ide/spyder-kernels/pull/233) - PR: Don't save file in runcell, by [@impact27](https://github.com/impact27) In this release 3 pull requests were closed. ---- ## Version 1.9.3 (2020-07-24) ### Issues Closed * [Issue 236](https://github.com/spyder-ide/spyder-kernels/issues/236) - test_dask_multiprocessing fails with pip packages ([PR 237](https://github.com/spyder-ide/spyder-kernels/pull/237) by [@bnavigator](https://github.com/bnavigator)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 237](https://github.com/spyder-ide/spyder-kernels/pull/237) - PR: Require dask[distributed] for tests, by [@bnavigator](https://github.com/bnavigator) ([236](https://github.com/spyder-ide/spyder-kernels/issues/236)) In this release 1 pull request was closed. ---- ## Version 1.9.2 (2020-07-10) ### Pull Requests Merged * [PR 234](https://github.com/spyder-ide/spyder-kernels/pull/234) - PR: Fix a problem caused by ipykernel 5.3.1, by [@ccordoba12](https://github.com/ccordoba12) * [PR 231](https://github.com/spyder-ide/spyder-kernels/pull/231) - PR: Send comm config on every message, by [@impact27](https://github.com/impact27) * [PR 230](https://github.com/spyder-ide/spyder-kernels/pull/230) - PR: Send comm config before any wait just to be sure, by [@impact27](https://github.com/impact27) * [PR 229](https://github.com/spyder-ide/spyder-kernels/pull/229) - PR: Add warning on console if file is not saved, by [@impact27](https://github.com/impact27) * [PR 228](https://github.com/spyder-ide/spyder-kernels/pull/228) - PR: Fix post_mortem interaction, by [@dalthviz](https://github.com/dalthviz) * [PR 227](https://github.com/spyder-ide/spyder-kernels/pull/227) - PR: Create a constant for numeric Numpy types, by [@dalthviz](https://github.com/dalthviz) * [PR 226](https://github.com/spyder-ide/spyder-kernels/pull/226) - PR: Backport PR 225, by [@ccordoba12](https://github.com/ccordoba12) * [PR 222](https://github.com/spyder-ide/spyder-kernels/pull/222) - PR: Remove the current working directory from sys.path for Python 3.7+, by [@ccordoba12](https://github.com/ccordoba12) * [PR 220](https://github.com/spyder-ide/spyder-kernels/pull/220) - PR: Make multithreading patch work for all OSes in Python 3, by [@steff456](https://github.com/steff456) ([12465](https://github.com/spyder-ide/spyder/issues/12465)) In this release 9 pull requests were closed. ---- ## Version 1.9.1 (2020-05-06) ### Issues Closed * [Issue 217](https://github.com/spyder-ide/spyder-kernels/issues/217) - Maximum recursion depth exceeded ([PR 218](https://github.com/spyder-ide/spyder-kernels/pull/218) by [@ccordoba12](https://github.com/ccordoba12)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 219](https://github.com/spyder-ide/spyder-kernels/pull/219) - PR: Check that startup file exists, by [@impact27](https://github.com/impact27) ([12442](https://github.com/spyder-ide/spyder/issues/12442)) * [PR 218](https://github.com/spyder-ide/spyder-kernels/pull/218) - PR: Avoid an error when computing the shape of Pandas objects, by [@ccordoba12](https://github.com/ccordoba12) ([217](https://github.com/spyder-ide/spyder-kernels/issues/217)) * [PR 215](https://github.com/spyder-ide/spyder-kernels/pull/215) - PR: Fix post mortem functionality, by [@impact27](https://github.com/impact27) * [PR 212](https://github.com/spyder-ide/spyder-kernels/pull/212) - PR: Set namespace correctly when running in new namespace, by [@impact27](https://github.com/impact27) * [PR 211](https://github.com/spyder-ide/spyder-kernels/pull/211) - PR: Use Exception instead of ImportError in is_special_kernel_valid, by [@ccordoba12](https://github.com/ccordoba12) In this release 5 pull requests were closed. ---- ## Version 1.9.0 (2020-03-14) ### New features * Allow IPython magics in code again. * Allow PyQt applications to be run multiple times. * Remove `__file__` after running a file. ### Pull Requests Merged * [PR 209](https://github.com/spyder-ide/spyder-kernels/pull/209) - PR: Improve message about using invalid syntax in cells and files, by [@ccordoba12](https://github.com/ccordoba12) * [PR 208](https://github.com/spyder-ide/spyder-kernels/pull/208) - PR: Add flag to override data and handle NpzFile instances, by [@dalthviz](https://github.com/dalthviz) * [PR 206](https://github.com/spyder-ide/spyder-kernels/pull/206) - PR: Move CI to github actions on branch 1.x, by [@goanpeca](https://github.com/goanpeca) * [PR 204](https://github.com/spyder-ide/spyder-kernels/pull/204) - PR: Remove load_exception in CommBase, by [@impact27](https://github.com/impact27) * [PR 200](https://github.com/spyder-ide/spyder-kernels/pull/200) - PR: Fix %varexp namespace, by [@impact27](https://github.com/impact27) ([6695](https://github.com/spyder-ide/spyder/issues/6695)) * [PR 199](https://github.com/spyder-ide/spyder-kernels/pull/199) - PR: Prevent completion from changing local objects while debugging, by [@impact27](https://github.com/impact27) * [PR 198](https://github.com/spyder-ide/spyder-kernels/pull/198) - PR: Add method to check dependencies for special consoles, by [@dalthviz](https://github.com/dalthviz) * [PR 195](https://github.com/spyder-ide/spyder-kernels/pull/195) - PR: Give access to the running namespace when refreshing variables, by [@impact27](https://github.com/impact27) * [PR 193](https://github.com/spyder-ide/spyder-kernels/pull/193) - PR: Update test to work with IPython 7.10+, by [@impact27](https://github.com/impact27) * [PR 192](https://github.com/spyder-ide/spyder-kernels/pull/192) - PR: Resend comm configuration on timeout, by [@impact27](https://github.com/impact27) * [PR 190](https://github.com/spyder-ide/spyder-kernels/pull/190) - PR: Allow IPython magics in code again, by [@impact27](https://github.com/impact27) ([11023](https://github.com/spyder-ide/spyder/issues/11023)) * [PR 189](https://github.com/spyder-ide/spyder-kernels/pull/189) - PR: Patch PyQt to save created QApplication instances, by [@impact27](https://github.com/impact27) ([2970](https://github.com/spyder-ide/spyder/issues/2970)) * [PR 187](https://github.com/spyder-ide/spyder-kernels/pull/187) - PR: Remove `__file__` after running script, by [@impact27](https://github.com/impact27) ([1918](https://github.com/spyder-ide/spyder/issues/1918)) In this release 13 pull requests were closed. ---- ## Version 1.8.1 (2019-12-05) ### Pull Requests Merged * [PR 185](https://github.com/spyder-ide/spyder-kernels/pull/185) - PR: Process first frame in Pdb In this release 1 pull request was closed. ---- ## Version 1.8.0 (2019-11-18) ### New features * Add an option to exclude callables and modules in namespace view. * Add methods to update `sys.path` from Spyder. * Add an option to execute IPython events in Pdb. ### Pull Requests Merged * [PR 183](https://github.com/spyder-ide/spyder-kernels/pull/183) - PR: Add an option to namespace view settings to exclude callables and modules * [PR 182](https://github.com/spyder-ide/spyder-kernels/pull/182) - PR: Use IPython completer for Pdb * [PR 181](https://github.com/spyder-ide/spyder-kernels/pull/181) - PR: Add path update methods * [PR 180](https://github.com/spyder-ide/spyder-kernels/pull/180) - PR: Cleanup spydercustomize * [PR 179](https://github.com/spyder-ide/spyder-kernels/pull/179) - PR: Use a timeout in CommBase if a call passes one different from None * [PR 178](https://github.com/spyder-ide/spyder-kernels/pull/178) - PR: Correctly set namespace while debugging * [PR 175](https://github.com/spyder-ide/spyder-kernels/pull/175) - PR: Add an option to execute IPython events in Pdb * [PR 174](https://github.com/spyder-ide/spyder-kernels/pull/174) - PR: Prevent pdb syntax error from blocking the console ([10588](https://github.com/spyder-ide/spyder/issues/10588)) In this release 8 pull requests were closed. ---- ## Version 1.7.0 (2019-11-02) ### New features * Create a new ZMQ socket for comms. * Allow different frontends to have different pickle protocols. * Add a way to ignore installed Python libraries while debugging ### Pull Requests Merged * [PR 177](https://github.com/spyder-ide/spyder-kernels/pull/177) - PR: Update ipykernel required version ([2902](https://github.com/spyder-ide/spyder/issues/2902)) * [PR 176](https://github.com/spyder-ide/spyder-kernels/pull/176) - PR: Improve displayed type and value for generic objects * [PR 169](https://github.com/spyder-ide/spyder-kernels/pull/169) - PR: Create a Comm socket * [PR 168](https://github.com/spyder-ide/spyder-kernels/pull/168) - PR: Require more recent version of jupyter-client * [PR 167](https://github.com/spyder-ide/spyder-kernels/pull/167) - PR: Don't demand that a file exists in Pdb * [PR 166](https://github.com/spyder-ide/spyder-kernels/pull/166) - PR: Allow different frontends to have different pickle protocols * [PR 152](https://github.com/spyder-ide/spyder-kernels/pull/152) - PR: Add a way to ignore installed Python libraries while debugging In this release 7 pull requests were closed. ---- ## Version 1.6.0 (2019-10-16) ### New features * Allow IPython magics in Pdb. * Allow Pdb to run multiline statments. * Make `runfile` to retrieve code from Spyder. * Add code completion to Pdb. ### Issues Closed * [Issue 139](https://github.com/spyder-ide/spyder-kernels/issues/139) - Regression: runfile doesn't execute ipython magic ([PR 143](https://github.com/spyder-ide/spyder-kernels/pull/143)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 163](https://github.com/spyder-ide/spyder-kernels/pull/163) - PR: Fix tests that use setup_kernel * [PR 162](https://github.com/spyder-ide/spyder-kernels/pull/162) - PR: Improve Pdb sigint handler * [PR 161](https://github.com/spyder-ide/spyder-kernels/pull/161) - PR: Fix post-mortem debugging * [PR 157](https://github.com/spyder-ide/spyder-kernels/pull/157) - PR: Fix breakpoint update ([10290](https://github.com/spyder-ide/spyder/issues/10290)) * [PR 154](https://github.com/spyder-ide/spyder-kernels/pull/154) - PR: Allow IPython magics in Pdb * [PR 153](https://github.com/spyder-ide/spyder-kernels/pull/153) - PR: Add a setting to disable printing the stack on every Pdb command * [PR 151](https://github.com/spyder-ide/spyder-kernels/pull/151) - PR: Remove Pdb Monkeypatching * [PR 148](https://github.com/spyder-ide/spyder-kernels/pull/148) - PR: Allow Pdb to run multiline statments * [PR 143](https://github.com/spyder-ide/spyder-kernels/pull/143) - PR: Make runfile to retrieve code from the Spyder editor and add it to linecache ([1643](https://github.com/spyder-ide/spyder/issues/1643), [139](https://github.com/spyder-ide/spyder-kernels/issues/139)) * [PR 133](https://github.com/spyder-ide/spyder-kernels/pull/133) - PR: Add code completion to Pdb In this release 10 pull requests were closed. ---- ## Version 1.5.0 (2019-09-15) ### New features * Add a new debugcell builtin command. * Make runfile work in an empty namespace by default. * Improve the display of tracebacks. * Use the highest pickle protocol available to serialize data. * Use Jupyter comms to communicate with the Spyder frontend. * This release also contains all fixes present in versions 0.5.1 and 0.5.2. ### Issues Closed * [Issue 147](https://github.com/spyder-ide/spyder-kernels/issues/147) - debugfile() got an unexpected keyword argument 'current_namespace' ([PR 150](https://github.com/spyder-ide/spyder-kernels/pull/150)) * [Issue 145](https://github.com/spyder-ide/spyder-kernels/issues/145) - KeyError on comms when restarting the kernel ([PR 146](https://github.com/spyder-ide/spyder-kernels/pull/146)) * [Issue 97](https://github.com/spyder-ide/spyder-kernels/issues/97) - Can't repeat runcell from terminal ([PR 112](https://github.com/spyder-ide/spyder-kernels/pull/112)) * [Issue 73](https://github.com/spyder-ide/spyder-kernels/issues/73) - Select a higher Pickle protocol in case Spyder and the kernel are running in Python 3.4+ ([PR 111](https://github.com/spyder-ide/spyder-kernels/pull/111)) In this release 4 issues were closed. ### Pull Requests Merged * [PR 150](https://github.com/spyder-ide/spyder-kernels/pull/150) - PR: Add current_namespace kwarg to debugfile ([147](https://github.com/spyder-ide/spyder-kernels/issues/147)) * [PR 146](https://github.com/spyder-ide/spyder-kernels/pull/146) - PR: Set closed flag before deleting comms ([145](https://github.com/spyder-ide/spyder-kernels/issues/145)) * [PR 144](https://github.com/spyder-ide/spyder-kernels/pull/144) - PR: Solve error with exit command not being defined in the debugger * [PR 142](https://github.com/spyder-ide/spyder-kernels/pull/142) - PR: Set debug state before asking for input * [PR 140](https://github.com/spyder-ide/spyder-kernels/pull/140) - PR: Update jupyter-client minimal required version * [PR 137](https://github.com/spyder-ide/spyder-kernels/pull/137) - PR: Add a way to change foreground color of Sympy repr's * [PR 136](https://github.com/spyder-ide/spyder-kernels/pull/136) - PR: Ask the frontend to save files before running them * [PR 134](https://github.com/spyder-ide/spyder-kernels/pull/134) - PR: Improve the display of tracebacks and better handle namespace and __file__ during execution * [PR 131](https://github.com/spyder-ide/spyder-kernels/pull/131) - PR: Make runfile work in an empty namespace * [PR 130](https://github.com/spyder-ide/spyder-kernels/pull/130) - PR: Add .pickle files to .gitignore * [PR 128](https://github.com/spyder-ide/spyder-kernels/pull/128) - PR: Fix deprecated import * [PR 112](https://github.com/spyder-ide/spyder-kernels/pull/112) - PR: Use the comms API to improve runcell and add a new debugcell command ([97](https://github.com/spyder-ide/spyder-kernels/issues/97)) * [PR 111](https://github.com/spyder-ide/spyder-kernels/pull/111) - PR: Use Jupyter comms to communicate with the Spyder frontend ([73](https://github.com/spyder-ide/spyder-kernels/issues/73)) In this release 13 pull requests were closed. ---- ## Version 1.4.0 (2019-06-24) ### New features * Add entries necessary for the new Object Explorer to REMOTE_SETTINGS. * This release also contains all features and fixes present in version 0.5.0 ### Pull Requests Merged * [PR 100](https://github.com/spyder-ide/spyder-kernels/pull/100) - PR: Add object explorer settings to REMOTE_SETTINGS In this release 1 pull request was closed. ---- ## Version 1.3.3 (2019-04-08) ### New features * This release contains all features and fixes present in versions 0.4.3 and 0.4.4 ### Issues Closed * [Issue 93](https://github.com/spyder-ide/spyder-kernels/issues/93) - test_np_threshold is failing ([PR 95](https://github.com/spyder-ide/spyder-kernels/pull/95)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 95](https://github.com/spyder-ide/spyder-kernels/pull/95) - PR: Change np.nan for np.inf in test_np_threshold ([93](https://github.com/spyder-ide/spyder-kernels/issues/93)) In this release 1 pull request was closed. ---- ## Version 1.3.2 (2019-02-10) Sister release for 0.4.2 ---- ## Version 1.3.1 (2019-02-03) Sister release for 0.4.1 ---- ## Version 1.3.0 (2019-02-02) ### New features * Make runcell set __file__ to the path of the file containing the cell * This release also contains all features and fixes present in version 0.4. ### Issues Closed * [Issue 78](https://github.com/spyder-ide/spyder-kernels/issues/78) - Nopython jit of numba is not working in runcell ([PR 79](https://github.com/spyder-ide/spyder-kernels/pull/79)) * [Issue 76](https://github.com/spyder-ide/spyder-kernels/issues/76) - Detect the name of the file currently running the cell ([PR 77](https://github.com/spyder-ide/spyder-kernels/pull/77)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 79](https://github.com/spyder-ide/spyder-kernels/pull/79) - PR: Remove user module reloader from runcell ([78](https://github.com/spyder-ide/spyder-kernels/issues/78)) * [PR 77](https://github.com/spyder-ide/spyder-kernels/pull/77) - PR: Have runcell set __file__ to the path of the file containing the cell ([76](https://github.com/spyder-ide/spyder-kernels/issues/76)) * [PR 72](https://github.com/spyder-ide/spyder-kernels/pull/72) - PR: Fix numpy printoptions format ([7885](https://github.com/spyder-ide/spyder/issues/7885)) In this release 3 pull requests were closed. ---- ## Version 1.2 (2018-12-26) ### New features * Add the `runcell` command to run cells from Spyder's editor without pasting their contents in the console. * This release also contains all features and fixes present in version 0.3. ### Issues Closed * [Issue 57](https://github.com/spyder-ide/spyder-kernels/issues/57) - Add a test for runcell ([PR 70](https://github.com/spyder-ide/spyder-kernels/pull/70)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 70](https://github.com/spyder-ide/spyder-kernels/pull/70) - PR: Add a test for the runcell command ([57](https://github.com/spyder-ide/spyder-kernels/issues/57)) * [PR 69](https://github.com/spyder-ide/spyder-kernels/pull/69) - PR: Start testing in macOS * [PR 67](https://github.com/spyder-ide/spyder-kernels/pull/67) - PR: Drop using ci-helpers in our CIs * [PR 58](https://github.com/spyder-ide/spyder-kernels/pull/58) - PR: runcell trigger post_execute before run_cell to end the run_cell pre_execute * [PR 7](https://github.com/spyder-ide/spyder-kernels/pull/7) - PR: Add runcell to spydercustomize In this release 5 pull requests were closed. ---- ## Version 1.1 (2018-08-11) ### Issues Closed * [Issue 14](https://github.com/spyder-ide/spyder-kernels/issues/14) - Startup lines to split with semicolon instead of comma ([PR 15](https://github.com/spyder-ide/spyder-kernels/pull/15)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 15](https://github.com/spyder-ide/spyder-kernels/pull/15) - PR: Separate startup run_lines with semicolon instead of comma ([14](https://github.com/spyder-ide/spyder-kernels/issues/14)) In this release 1 pull request was closed. ---- ## Version 1.0.3 (2018-08-09) Sister release for 0.2.6 ---- ## Version 1.0.2 (2018-08-09) Sister release for 0.2.5 ---- ## Version 1.0.1 (2018-06-25) Sister release for 0.2.4 ---- ## Version 1.0.0 (2018-06-24) Initial release for Spyder 4 ---- ## Version 0.5.2 (2019-09-15) ### Issues Closed * [Issue 132](https://github.com/spyder-ide/spyder-kernels/issues/132) - tests use removed pandas.Panel ([PR 135](https://github.com/spyder-ide/spyder-kernels/pull/135)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 149](https://github.com/spyder-ide/spyder-kernels/pull/149) - PR: Add xarray to our test deps in setup.py * [PR 135](https://github.com/spyder-ide/spyder-kernels/pull/135) - PR: Replace usage of Pandas Panel for Xarray Dataset in our tests ([132](https://github.com/spyder-ide/spyder-kernels/issues/132)) In this release 2 pull requests were closed. ---- ## Version 0.5.1 (2019-07-11) ### Issues Closed * [Issue 121](https://github.com/spyder-ide/spyder-kernels/issues/121) - Add test requirements to setup.py ([PR 122](https://github.com/spyder-ide/spyder-kernels/pull/122)) * [Issue 120](https://github.com/spyder-ide/spyder-kernels/issues/120) - Backport CI configuration from master to 0.x ([PR 123](https://github.com/spyder-ide/spyder-kernels/pull/123)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 125](https://github.com/spyder-ide/spyder-kernels/pull/125) - PR: Fix not including tests in tarballs and wheels * [PR 123](https://github.com/spyder-ide/spyder-kernels/pull/123) - PR: Backport CI configuration from master to 0.x ([120](https://github.com/spyder-ide/spyder-kernels/issues/120)) * [PR 122](https://github.com/spyder-ide/spyder-kernels/pull/122) - PR: Add tests requirements to setup.py ([121](https://github.com/spyder-ide/spyder-kernels/issues/121)) In this release 3 pull requests were closed. ---- ## Version 0.5.0 (2019-06-23) ### New features * Set Matplotlib backend to inline for kernels started in a terminal. * Handle option sent from Spyder to show/hide cmd windows generated by the subprocess module. ### Issues Closed * [Issue 108](https://github.com/spyder-ide/spyder-kernels/issues/108) - Set matplotlib backend to inline by default on starting a new kernel ([PR 110](https://github.com/spyder-ide/spyder-kernels/pull/110)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 110](https://github.com/spyder-ide/spyder-kernels/pull/110) - PR: Set Matplotlib backend to inline for kernels started outside Spyder ([108](https://github.com/spyder-ide/spyder-kernels/issues/108)) * [PR 107](https://github.com/spyder-ide/spyder-kernels/pull/107) - PR: Use Readme.md for long description in PyPi * [PR 104](https://github.com/spyder-ide/spyder-kernels/pull/104) - PR: Handle option to show/hide cmd windows generated by the subprocess module In this release 3 pull requests were closed. ---- ## Version 0.4.4 (2019-04-08) ### Issues Closed * [Issue 102](https://github.com/spyder-ide/spyder-kernels/issues/102) - Tkinter is now required for version 0.4.3 after patching the turtle code ([PR 103](https://github.com/spyder-ide/spyder-kernels/pull/103)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 106](https://github.com/spyder-ide/spyder-kernels/pull/106) - PR: Skip test_turtle_launch if Tk is not installed * [PR 103](https://github.com/spyder-ide/spyder-kernels/pull/103) - PR: Enclose turtle customizations in a try/except to avoid a dependency on Tk ([102](https://github.com/spyder-ide/spyder-kernels/issues/102)) In this release 2 pull requests were closed. ---- ## Version 0.4.3 (2019-03-31) ### Issues Closed * [Issue 91](https://github.com/spyder-ide/spyder-kernels/issues/91) - KeyError when running "%reset -f" programmatically ([PR 96](https://github.com/spyder-ide/spyder-kernels/pull/96)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 96](https://github.com/spyder-ide/spyder-kernels/pull/96) - PR: Avoid error when trying to pop __file__ out of the current namespace ([91](https://github.com/spyder-ide/spyder-kernels/issues/91)) * [PR 92](https://github.com/spyder-ide/spyder-kernels/pull/92) - PR: Include user site-packages directory in the list of excluded paths by the UMR ([8776](https://github.com/spyder-ide/spyder/issues/8776)) * [PR 90](https://github.com/spyder-ide/spyder-kernels/pull/90) - PR: Patch turtle.bye to make it work with multiple runnings of the same code ([6278](https://github.com/spyder-ide/spyder/issues/6278)) In this release 3 pull requests were closed. ---- ## Version 0.4.2 (2019-02-07) ### Issues Closed * [Issue 85](https://github.com/spyder-ide/spyder-kernels/issues/85) - NameError: name 'modpath' is not defined ([PR 86](https://github.com/spyder-ide/spyder-kernels/pull/86)) In this release 1 issue was closed. ### Pull Requests Merged * [PR 88](https://github.com/spyder-ide/spyder-kernels/pull/88) - PR: Improve Cython activation * [PR 87](https://github.com/spyder-ide/spyder-kernels/pull/87) - PR: Fix running Cython files * [PR 86](https://github.com/spyder-ide/spyder-kernels/pull/86) - PR: Fix problems with UMR's run method ([85](https://github.com/spyder-ide/spyder-kernels/issues/85)) In this release 3 pull requests were closed. ---- ## Version 0.4.1 (2019-02-03) ### Pull Requests Merged * [PR 84](https://github.com/spyder-ide/spyder-kernels/pull/84) - PR: Better way to skip standard library and site-packages modules from UMR * [PR 83](https://github.com/spyder-ide/spyder-kernels/pull/83) - PR: Blacklist tensorflow from the UMR ([8697](https://github.com/spyder-ide/spyder/issues/8697)) In this release 2 pull requests were closed. ---- ## Version 0.4 (2019-02-02) ### New features * This release fixes several important issues that prevented saving the current namespace to work as expected. ### Issues Closed * [Issue 75](https://github.com/spyder-ide/spyder-kernels/issues/75) - Namespace serialization silently fails if any object is unserializable, e.g. a Python module ([PR 81](https://github.com/spyder-ide/spyder-kernels/pull/81)) * [Issue 9](https://github.com/spyder-ide/spyder-kernels/issues/9) - Spydata files won't import if the original filename is changed ([PR 80](https://github.com/spyder-ide/spyder-kernels/pull/80)) In this release 2 issues were closed. ### Pull Requests Merged * [PR 82](https://github.com/spyder-ide/spyder-kernels/pull/82) - PR: Enclose calls to load wurlitzer and autoreload in try/except's ([8668](https://github.com/spyder-ide/spyder/issues/8668)) * [PR 81](https://github.com/spyder-ide/spyder-kernels/pull/81) - PR: Fix and improve saving of Spyder namespace with many types of objects ([75](https://github.com/spyder-ide/spyder-kernels/issues/75)) * [PR 80](https://github.com/spyder-ide/spyder-kernels/pull/80) - PR: Fix loading Spydata file with changed filename and other edge-cases in load_dict ([9](https://github.com/spyder-ide/spyder-kernels/issues/9)) In this release 3 pull requests were closed. ---- ## Version 0.3 (2018-11-23) ### New features * Add Wurlitzer as a new dependency on Posix systems. * Add tests for the console kernel. ### Issues Closed * [Issue 62](https://github.com/spyder-ide/spyder-kernels/issues/62) - Add support for AppVeyor ([PR 63](https://github.com/spyder-ide/spyder-kernels/pull/63)) * [Issue 60](https://github.com/spyder-ide/spyder-kernels/issues/60) - Only load Wurlitzer in Posix systems ([PR 64](https://github.com/spyder-ide/spyder-kernels/pull/64)) * [Issue 23](https://github.com/spyder-ide/spyder-kernels/issues/23) - Add tests for the console kernel ([PR 37](https://github.com/spyder-ide/spyder-kernels/pull/37)) In this release 3 issues were closed. ### Pull Requests Merged * [PR 64](https://github.com/spyder-ide/spyder-kernels/pull/64) - PR: Don't load Wurlitzer extension on Windows because it has no effect there ([60](https://github.com/spyder-ide/spyder-kernels/issues/60)) * [PR 63](https://github.com/spyder-ide/spyder-kernels/pull/63) - PR: Test on Windows with Appveyor ([62](https://github.com/spyder-ide/spyder-kernels/issues/62)) * [PR 61](https://github.com/spyder-ide/spyder-kernels/pull/61) - PR: Patch multiprocessing to make it work when all variables are removed ([8128](https://github.com/spyder-ide/spyder/issues/8128)) * [PR 59](https://github.com/spyder-ide/spyder-kernels/pull/59) - PR: Filter deprecation warnings in ipykernel ([8103](https://github.com/spyder-ide/spyder/issues/8103)) * [PR 56](https://github.com/spyder-ide/spyder-kernels/pull/56) - PR: Add Wurlitzer to Readme * [PR 55](https://github.com/spyder-ide/spyder-kernels/pull/55) - PR: Exclude all tests from our tarballs * [PR 54](https://github.com/spyder-ide/spyder-kernels/pull/54) - PR: Add the Wurlitzer package to capture stdout/stderr from C libraries ([3777](https://github.com/spyder-ide/spyder/issues/3777)) * [PR 53](https://github.com/spyder-ide/spyder-kernels/pull/53) - PR: Remove current working directory from sys.path before starting the console kernel ([8007](https://github.com/spyder-ide/spyder/issues/8007)) * [PR 37](https://github.com/spyder-ide/spyder-kernels/pull/37) - PR: Initial tests for the console kernel ([23](https://github.com/spyder-ide/spyder-kernels/issues/23)) * [PR 36](https://github.com/spyder-ide/spyder-kernels/pull/36) - PR: Make tests to really fail in CircleCI * [PR 21](https://github.com/spyder-ide/spyder-kernels/pull/21) - PR: Add AUTHORS.txt in MANIFEST.in to include in package In this release 11 pull requests were closed. ---- ## Version 0.2.6 (2018-08-09) ### Pull Requests Merged * [PR 20](https://github.com/spyder-ide/spyder-kernels/pull/20) - PR: Include license file again in tarball In this release 1 pull request was closed. ---- ## Version 0.2.5 (2018-08-09) ### Pull Requests Merged * [PR 19](https://github.com/spyder-ide/spyder-kernels/pull/19) - PR: Fix inconsistent EOLs * [PR 18](https://github.com/spyder-ide/spyder-kernels/pull/18) - PR: Fix legal texts and make them consistent across all files * [PR 17](https://github.com/spyder-ide/spyder-kernels/pull/17) - PR: Add/update descriptions, links and metadata in setup.py * [PR 16](https://github.com/spyder-ide/spyder-kernels/pull/16) - PR: Include test suite in manifest * [PR 11](https://github.com/spyder-ide/spyder-kernels/pull/11) - PR: Add codecov support to see coverage * [PR 10](https://github.com/spyder-ide/spyder-kernels/pull/10) - PR: Start testing with CircleCI * [PR 8](https://github.com/spyder-ide/spyder-kernels/pull/8) - PR: Demand specific dependency versions needed by Spyder In this release 7 pull requests were closed. ---- ## Version 0.2.4 (2018-06-25) ### Pull Requests Merged * [PR 6](https://github.com/spyder-ide/spyder-kernels/pull/6) - PR: Handle deprecated 'summary' method for Pandas In this release 1 pull request was closed. ---- ## Version 0.2.3 (2018-06-23) ### Pull Requests Merged * [PR 5](https://github.com/spyder-ide/spyder-kernels/pull/5) - PR: Add __version__ to package's init In this release 1 pull request was closed. ---- ## Version 0.2.2 (2018-06-22) ### Pull Requests Merged * [PR 4](https://github.com/spyder-ide/spyder-kernels/pull/4) - PR: Fix debugging in Python 2 In this release 1 pull request was closed. ---- ## Version 0.2.1 (2018-06-22) ### Pull Requests Merged * [PR 3](https://github.com/spyder-ide/spyder-kernels/pull/3) - PR: Fix debugging In this release 1 pull request was closed. ---- ## Version 0.2 (2018-06-20) ### Pull Requests Merged * [PR 2](https://github.com/spyder-ide/spyder-kernels/pull/2) - PR: Import our customizations directly here * [PR 1](https://github.com/spyder-ide/spyder-kernels/pull/1) - PR: Fix some errors in sitecustomize In this release 2 pull requests were closed. ---- ## Version 0.1 (2018-06-18) Initial release spyder-kernels-3.0.3/LICENSE.txt000066400000000000000000000021171475072611100163140ustar00rootroot00000000000000MIT License Copyright (c) 2009- Spyder Kernels Contributors (see AUTHORS.txt) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. spyder-kernels-3.0.3/MANIFEST.in000066400000000000000000000001171475072611100162250ustar00rootroot00000000000000include AUTHORS.txt include CHANGELOG.md include LICENSE.txt include README.md spyder-kernels-3.0.3/README.md000066400000000000000000000054051475072611100157530ustar00rootroot00000000000000# Jupyter kernels for the Spyder console [![Windows status](https://github.com/spyder-ide/spyder-kernels/workflows/Windows%20tests/badge.svg)](https://github.com/spyder-ide/spyder-kernels/actions?query=workflow%3A%22Windows+tests%22) [![Linux status](https://github.com/spyder-ide/spyder-kernels/workflows/Linux%20tests/badge.svg)](https://github.com/spyder-ide/spyder-kernels/actions?query=workflow%3A%22Linux+tests%22) [![Macos status](https://github.com/spyder-ide/spyder-kernels/workflows/Macos%20tests/badge.svg)](https://github.com/spyder-ide/spyder-kernels/actions?query=workflow%3A%22Macos+tests%22) [![codecov](https://codecov.io/gh/spyder-ide/spyder-kernels/branch/master/graph/badge.svg)](https://codecov.io/gh/spyder-ide/spyder-kernels/branch/master) Package that provides Jupyter kernels for use with the consoles of Spyder, the Scientific Python Development Environment. These kernels can be launched either through Spyder itself or in an independent Python session, and allow for interactive or file-based execution of Python code inside Spyder. To learn about creating, connecting to and using these kernels with the Spyder console, please read our [documentation](https://docs.spyder-ide.org/current/panes/ipythonconsole.html). For advice on managing packages and environments with `spyder-kernels`, please read this [FAQ](http://docs.spyder-ide.org/current/faq.html#using-existing-environment) in our docs. ## Installation To install this package, you can use either the ``pip`` or ``conda`` package managers, as follows: Using conda (the recommended way!): ``` conda install spyder-kernels ``` Using pip: ``` pip install spyder-kernels ``` ## Dependencies This project depends on: * [ipykernel](https://github.com/ipython/ipykernel/) * [cloudpickle](https://github.com/cloudpipe/cloudpickle) * [wurlitzer](https://github.com/minrk/wurlitzer) (only on Linux and macOS). ## Changelog Visit our [CHANGELOG](CHANGELOG.md) file to know more about our new features and improvements. ## Development and contribution To start contributing to this project you can execute ``` pip install -e . ``` in your git clone and then test your changes in Spyder. We follow PEP8 and PEP257 style guidelines. ## Sponsors Spyder and its subprojects are funded thanks to the generous support of [![Chan Zuckerberg Initiative](https://raw.githubusercontent.com/spyder-ide/spyder/master/img_src/czi.png)](https://chanzuckerberg.com/)[![Numfocus](https://i2.wp.com/numfocus.org/wp-content/uploads/2017/07/NumFocus_LRG.png?fit=320%2C148&ssl=1)](https://numfocus.org/) and the donations we have received from our users around the world through [Open Collective](https://opencollective.com/spyder/): [![Sponsors](https://opencollective.com/spyder/sponsors.svg)](https://opencollective.com/spyder#support) spyder-kernels-3.0.3/RELEASE.md000066400000000000000000000012341475072611100160720ustar00rootroot00000000000000To release a new version of spyder-kernels on PyPI: * Close the respective milestone on Github * git checkout 3.x * git fetch upstream && get merge upstream/3.x * Update CHANGELOG.md with `loghub spyder-ide/spyder-kernels -m vX.X.X` * git clean -xfdi * Update `_version.py` (set release version, remove 'dev0') * git add . && git commit -m 'Release X.X.X' * python setup.py sdist * python setup.py bdist_wheel * twine check --strict dist/* * twine upload dist/* * git tag -a vX.X.X -m 'Release X.X.X' * Update `_version.py` (add 'dev0' and increment patch) * git add . && git commit -m 'Back to work' * git push upstream 3.x * git push upstream --tags spyder-kernels-3.0.3/requirements/000077500000000000000000000000001475072611100172135ustar00rootroot00000000000000spyder-kernels-3.0.3/requirements/posix.txt000066400000000000000000000002101475072611100211070ustar00rootroot00000000000000cloudpickle ipykernel>=6.29.3,<7 ipython>=8.12.2,<9 jupyter_client>=7.4.9,<9 pyzmq>=24.0.0 wurlitzer>=1.0.3 pyxdg>=0.26 setuptools<71.0 spyder-kernels-3.0.3/requirements/tests.txt000066400000000000000000000001511475072611100211130ustar00rootroot00000000000000cython dask flaky matplotlib mock numpy pandas pytest pytest-cov scipy xarray pillow django h5py pydicom spyder-kernels-3.0.3/requirements/windows.txt000066400000000000000000000001531475072611100214450ustar00rootroot00000000000000cloudpickle ipykernel>=6.29.3,<7 ipython>=8.12.2,<9 jupyter_client>=7.4.9,<9 pyzmq>=24.0.0 setuptools<71.0 spyder-kernels-3.0.3/setup.py000066400000000000000000000056071475072611100162120ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Jupyter Kernels for the Spyder consoles.""" # Standard library imports import ast import io import os # Third party imports from setuptools import find_packages, setup HERE = os.path.abspath(os.path.dirname(__file__)) with io.open('README.md', encoding='utf-8') as f: LONG_DESCRIPTION = f.read() def get_version(module='spyder_kernels'): """Get version.""" with open(os.path.join(HERE, module, '_version.py'), 'r') as f: data = f.read() lines = data.split('\n') for line in lines: if line.startswith('VERSION_INFO'): version_tuple = ast.literal_eval(line.split('=')[-1].strip()) version = '.'.join(map(str, version_tuple)) break return version REQUIREMENTS = [ 'cloudpickle', 'ipykernel>=6.29.3,<7', 'ipython>=8.12.2,<8.13; python_version=="3.8"', 'ipython>=8.13.0,<9,!=8.17.1; python_version>"3.8"', 'jupyter-client>=7.4.9,<9', 'pyzmq>=24.0.0', 'wurlitzer>=1.0.3;platform_system!="Windows"', 'pyxdg>=0.26;platform_system=="Linux"', ] TEST_REQUIREMENTS = [ 'cython', 'dask[distributed]', 'flaky', 'matplotlib', 'mock', 'numpy', 'pandas', 'pytest', 'pytest-cov', 'scipy', 'xarray', 'pillow', 'django', 'h5py', 'pydicom' ] setup( name='spyder-kernels', version=get_version(), keywords='spyder jupyter kernel ipython console', url='https://github.com/spyder-ide/spyder-kernels', download_url="https://www.spyder-ide.org/#fh5co-download", license='MIT', author='Spyder Development Team', author_email="spyderlib@googlegroups.com", description="Jupyter kernels for Spyder's console", long_description=LONG_DESCRIPTION, long_description_content_type='text/markdown', packages=find_packages(exclude=['docs', '*tests']), install_requires=REQUIREMENTS, extras_require={'test': TEST_REQUIREMENTS}, include_package_data=True, python_requires='>=3.8', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Framework :: Jupyter', 'Framework :: IPython', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Topic :: Software Development :: Interpreters', ] ) spyder-kernels-3.0.3/spyder_kernels/000077500000000000000000000000001475072611100175215ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/__init__.py000066400000000000000000000027041475072611100216350ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ MIT License Copyright (c) 2009- Spyder Kernels Contributors (see AUTHORS.txt) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from ._version import __version__ spyder-kernels-3.0.3/spyder_kernels/_version.py000066400000000000000000000006211475072611100217160ustar00rootroot00000000000000# # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Version File.""" VERSION_INFO = (3, 0, 3) __version__ = '.'.join(map(str, VERSION_INFO)) spyder-kernels-3.0.3/spyder_kernels/comms/000077500000000000000000000000001475072611100206375ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/comms/__init__.py000066400000000000000000000030041475072611100227450ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ API to communicate between the Spyder IDE and the Spyder kernel. It uses Jupyter Comms for messaging. The messages are sent by calling an arbitrary function, with the limitation that the arguments have to be picklable. If the function must return, the call must be blocking. In addition, the frontend can interrupt the kernel to process the message sent. This allows, for example, to set a breakpoint in pdb while the debugger is running. The message will only be delivered when the kernel is checking the event loop, or if pdb is waiting for an input. Example: On one side: ``` def hello_str(msg): print('Hello ' + msg + '!') def add(a, d): return a + b left_comm.register_call_handler('add_numbers', add) left_comm.register_call_handler('print_hello', hello_str) ``` On the other: ``` right_comm.remote_call().print_hello('world') res = right_comm.remote_call(blocking=True).add_numbers(1, 2) print('1 + 2 = ' + str(res)) ``` Which prints on the right side (The one with the `left_comm`): ``` Hello world! ``` And on the left side: ``` 1 + 2 = 3 ``` """ from spyder_kernels.comms.commbase import CommError spyder-kernels-3.0.3/spyder_kernels/comms/commbase.py000066400000000000000000000457031475072611100230100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) """ Class that handles communications between Spyder kernel and frontend. Comms transmit data in a list of buffers, and in a json-able dictionnary. Here, we only support json to avoid issues of compatibility between Python versions. In the abstraction below, buffers is used to send bytes. The messages exchanged have the following msg_dict: ``` msg_dict = { 'spyder_msg_type': spyder_msg_type, 'content': content, } ``` To simplify the usage of messaging, we use a higher level function calling mechanism: - The `remote_call` method returns a RemoteCallHandler object - By calling an attribute of this object, the call is sent to the other side of the comm. - If the `_wait_reply` is implemented, remote_call can be called with `blocking=True`, which will wait for a reply sent by the other side. The messages exchanged are: - Function call (spyder_msg_type = 'remote_call'): - The content is a dictionnary { 'call_name': The name of the function to be called, 'call_id': uuid to match the request to a potential reply, 'settings': A dictionnary of settings, 'call_args': The function args, 'call_kwargs': The function kwargs, 'buffered_args': The args index that are in the buffers, 'buffered_kwargs': the kwargs keys that are in the buffers } - The buffer contains any bytes in the arguments - If the 'settings' has `'blocking' = True`, a reply is sent. (spyder_msg_type = 'remote_call_reply'): - The 'content' is a dict with: { 'is_error': a boolean indicating if the return value is an exception to be raised. 'call_id': The uuid from above, 'call_name': The function name (mostly for debugging), 'call_return_value': The return value of the function } - The buffer contains the return value if it is bytes """ import logging import sys import uuid import traceback import builtins logger = logging.getLogger(__name__) # Max timeout (in secs) for blocking calls TIMEOUT = 3 class CommError(RuntimeError): pass def stacksummary_to_json(stack): """StackSummary to json.""" return [ { "filename": frame.filename, "lineno": frame.lineno, "name": frame.name, "line": frame.line } for frame in stack ] def staksummary_from_json(stack): """StackSummary from json.""" traceback.StackSummary.from_list([ ( frame["filename"], frame["lineno"], frame["name"], frame["line"] ) for frame in stack ]) class CommsErrorWrapper(): def __init__(self, call_name, call_id): self.call_name = call_name self.call_id = call_id self.etype, self.error, tb = sys.exc_info() self.tb = traceback.extract_tb(tb) def to_json(self): """Create JSON representation.""" return { "call_name": self.call_name, "call_id": self.call_id, "etype": self.etype.__name__, "args": self.error.args, "tb": stacksummary_to_json(self.tb) } @classmethod def from_json(cls, json_data): """Get a CommsErrorWrapper from a JSON representation.""" instance = cls.__new__(cls) instance.call_name = json_data["call_name"] instance.call_id = json_data["call_id"] etype = json_data["etype"] instance.etype = getattr( builtins, etype, type(etype, (Exception,), {}) ) instance.error = instance.etype(*json_data["args"]) instance.tb = staksummary_from_json(json_data["tb"]) return instance def raise_error(self): """ Raise the error while adding informations on the callback. """ # Add the traceback in the error, so it can be handled upstream raise self.etype(self) def format_error(self): """ Format the error received from the other side and returns a list of strings. """ lines = (['Exception in comms call {}:\n'.format(self.call_name)] + traceback.format_list(self.tb) + traceback.format_exception_only(self.etype, self.error)) return lines def print_error(self, file=None): """ Print the error to file or to sys.stderr if file is None. """ if file is None: file = sys.stderr for line in self.format_error(): print(line, file=file) def __str__(self): """Get string representation.""" return str(self.error) def __repr__(self): """Get repr.""" return repr(self.error) # Replace sys.excepthook to handle CommsErrorWrapper sys_excepthook = sys.excepthook def comm_excepthook(type, value, tb): if len(value.args) == 1 and isinstance(value.args[0], CommsErrorWrapper): traceback.print_tb(tb) value.args[0].print_error() return sys_excepthook(type, value, tb) sys.excepthook = comm_excepthook class CommBase: """ Class with the necessary attributes and methods to handle communications between a kernel and a frontend. Subclasses must open a comm and register it with `self._register_comm`. """ def __init__(self): super(CommBase, self).__init__() self.calling_comm_id = None self._comms = {} # Handlers self._message_handlers = {} self._remote_call_handlers = {} # Lists of reply numbers self._reply_inbox = {} self._reply_waitlist = {} self._register_message_handler( 'remote_call', self._handle_remote_call) self._register_message_handler( 'remote_call_reply', self._handle_remote_call_reply) def get_comm_id_list(self, comm_id=None): """Get a list of comms id.""" if comm_id is None: id_list = list(self._comms.keys()) else: id_list = [comm_id] return id_list def close(self, comm_id=None): """Close the comm and notify the other side.""" id_list = self.get_comm_id_list(comm_id) for comm_id in id_list: try: self._comms[comm_id]['comm'].close() del self._comms[comm_id] except KeyError: pass def is_open(self, comm_id=None): """Check to see if the comm is open.""" if comm_id is None: return len(self._comms) > 0 return comm_id in self._comms def register_call_handler(self, call_name, handler): """ Register a remote call handler. Parameters ---------- call_name : str The name of the called function. handler : callback A function to handle the request. """ self._remote_call_handlers[call_name] = handler def unregister_call_handler(self, call_name): """ Unegister a remote call handler. Parameters ---------- call_name : str The name of the called function. """ self._remote_call_handlers.pop(call_name, None) def remote_call(self, comm_id=None, callback=None, **settings): """Get a handler for remote calls.""" return RemoteCallFactory(self, comm_id, callback, **settings) # ---- Private ----- def _send_message( self, spyder_msg_type, content=None, comm_id=None, buffers=None ): """ Publish custom messages to the other side. Parameters ---------- spyder_msg_type: str The spyder message type content: dict The (JSONable) content of the message comm_id: int the comm to send to. If None sends to all comms. buffers: list(bytes) a list of bytes to send. """ if not self.is_open(comm_id): raise CommError("The comm is not connected.") id_list = self.get_comm_id_list(comm_id) for comm_id in id_list: msg_dict = { 'spyder_msg_type': spyder_msg_type, 'content': content, } self._comms[comm_id]['comm'].send(msg_dict, buffers=buffers) @property def _comm_name(self): """ Get the name used for the underlying comms. """ return 'spyder_api' def _register_message_handler(self, message_id, handler): """ Register a message handler. Parameters ---------- message_id : str The identifier for the message handler : callback A function to handle the message. This is called with: - msg_dict: A dictionary with message information. Pass None to unregister the message_id """ if handler is None: self._message_handlers.pop(message_id, None) return self._message_handlers[message_id] = handler def _register_comm(self, comm): """ Open a new comm to the kernel. """ comm.on_msg(self._comm_message) comm.on_close(self._comm_close) self._comms[comm.comm_id] = { 'comm': comm, 'status': 'opening', } def _comm_close(self, msg): """Close comm.""" comm_id = msg['content']['comm_id'] del self._comms[comm_id] def _comm_message(self, msg): """ Handle internal spyder messages. """ self.calling_comm_id = msg['content']['comm_id'] # Get message dict msg_dict = msg['content']['data'] spyder_msg_type = msg_dict['spyder_msg_type'] buffers = msg['buffers'] if spyder_msg_type in self._message_handlers: self._message_handlers[spyder_msg_type](msg_dict, buffers) else: logger.debug("No such spyder message type: %s" % spyder_msg_type) def _handle_remote_call(self, msg, buffers): """Handle a remote call.""" msg_dict = msg['content'] self.on_incoming_call(msg_dict) try: # read buffers args = msg_dict['call_args'] kwargs = msg_dict['call_kwargs'] if buffers: for idx in msg_dict['buffered_args']: args[idx] = buffers.pop(0) for name in msg_dict['buffered_kwargs']: kwargs[name] = buffers.pop(0) assert len(buffers) == 0 return_value = self._remote_callback( msg_dict['call_name'], args, kwargs ) self._set_call_return_value(msg_dict, return_value) except Exception: exc_infos = CommsErrorWrapper( msg_dict['call_name'], msg_dict['call_id']) self._set_call_return_value(msg_dict, exc_infos, is_error=True) def _remote_callback(self, call_name, call_args, call_kwargs): """Call the callback function for the remote call.""" if call_name in self._remote_call_handlers: return self._remote_call_handlers[call_name]( *call_args, **call_kwargs) raise CommError("No such spyder call type: %s" % call_name) def _set_call_return_value(self, call_dict, return_value, is_error=False): """ A remote call has just been processed. This will reply if settings['blocking'] == True """ settings = call_dict['settings'] display_error = ('display_error' in settings and settings['display_error']) if is_error: if display_error: return_value.print_error() return_value = return_value.to_json() send_reply = 'send_reply' in settings and settings['send_reply'] if not send_reply: # Nothing to send back return buffers = None if isinstance(return_value, bytes): buffers = [return_value] return_value = None content = { 'is_error': is_error, 'call_id': call_dict['call_id'], 'call_name': call_dict['call_name'], 'call_return_value': return_value } self._send_message( 'remote_call_reply', content=content, comm_id=self.calling_comm_id, buffers=buffers ) def _register_call(self, call_dict, callback=None): """ Register the call so the reply can be properly treated. """ settings = call_dict['settings'] blocking = 'blocking' in settings and settings['blocking'] call_id = call_dict['call_id'] if blocking or callback is not None: self._reply_waitlist[call_id] = blocking, callback def on_outgoing_call(self, call_dict): """A message is about to be sent""" return call_dict def on_incoming_call(self, call_dict): """A call was received""" pass def _send_call(self, call_dict, comm_id, buffers=None): """Send call.""" call_dict = self.on_outgoing_call(call_dict) self._send_message( 'remote_call', content=call_dict, comm_id=comm_id, buffers=buffers ) def _get_call_return_value(self, call_dict, comm_id): """ Send a remote call and return the reply. If settings['blocking'] == True, this will wait for a reply and return the replied value. """ settings = call_dict['settings'] blocking = 'blocking' in settings and settings['blocking'] if not blocking: return call_id = call_dict['call_id'] call_name = call_dict['call_name'] # Wait for the blocking call if 'timeout' in settings and settings['timeout'] is not None: timeout = settings['timeout'] else: timeout = TIMEOUT self._wait_reply(comm_id, call_id, call_name, timeout) content = self._reply_inbox.pop(call_id) return_value = content['call_return_value'] if content['is_error']: return self._sync_error(return_value) return return_value def _wait_reply(self, comm_id, call_id, call_name, timeout): """ Wait for the other side reply. """ raise NotImplementedError def _handle_remote_call_reply(self, msg_dict, buffers): """ A blocking call received a reply. """ content = msg_dict['content'] call_id = content['call_id'] call_name = content['call_name'] is_error = content['is_error'] return_value = content['call_return_value'] # Prepare return value if is_error: return_value = CommsErrorWrapper.from_json(return_value) elif buffers: assert len(buffers) == 1 return_value = buffers[0] content['call_return_value'] = return_value # Unexpected reply if call_id not in self._reply_waitlist: if is_error: return self._async_error(return_value) else: logger.debug('Got an unexpected reply {}, id:{}'.format( call_name, call_id)) return blocking, callback = self._reply_waitlist.pop(call_id) # Async error if is_error and not blocking: return self._async_error(return_value) # Callback if callback is not None and not is_error: callback(return_value) # Blocking inbox if blocking: self._reply_inbox[call_id] = content def _async_error(self, error_wrapper): """ Handle an error that was raised on the other side asyncronously. """ error_wrapper.print_error() def _sync_error(self, error_wrapper): """ Handle an error that was raised on the other side syncronously. """ error_wrapper.raise_error() class RemoteCallFactory: """Class to create `RemoteCall`s.""" def __init__(self, comms_wrapper, comm_id, callback, **settings): # Avoid setting attributes super(RemoteCallFactory, self).__setattr__( '_comms_wrapper', comms_wrapper) super(RemoteCallFactory, self).__setattr__('_comm_id', comm_id) super(RemoteCallFactory, self).__setattr__('_callback', callback) super(RemoteCallFactory, self).__setattr__('_settings', settings) def __getattr__(self, name): """Get a call for a function named 'name'.""" return RemoteCall(name, self._comms_wrapper, self._comm_id, self._callback, self._settings) def __setattr__(self, name, value): """Set an attribute to the other side.""" raise NotImplementedError class RemoteCall(): """Class to call the other side of the comms like a function.""" def __init__(self, name, comms_wrapper, comm_id, callback, settings): self._name = name self._comms_wrapper = comms_wrapper self._comm_id = comm_id self._settings = settings self._callback = callback def __call__(self, *args, **kwargs): """ Transmit the call to the other side of the tunnel. The args and kwargs have to be JSON-serializable or bytes. """ blocking = 'blocking' in self._settings and self._settings['blocking'] self._settings['send_reply'] = blocking or self._callback is not None # The call will be serialized with json. The bytes are sent separately. buffers = [] buffered_args = [] buffered_kwargs = [] args = list(args) for i, arg in enumerate(args): if isinstance(arg, bytes): buffers.append(arg) buffered_args.append(i) args[i] = None for name in kwargs: arg = kwargs[name] if isinstance(arg, bytes): buffers.append(arg) buffered_kwargs.append(name) kwargs[name] = None call_id = uuid.uuid4().hex call_dict = { 'call_name': self._name, 'call_id': call_id, 'settings': self._settings, 'call_args': args, 'call_kwargs': kwargs, 'buffered_args': buffered_args, 'buffered_kwargs': buffered_kwargs } if not self._comms_wrapper.is_open(self._comm_id): # Only an error if the call is blocking. if blocking: raise CommError("The comm is not connected.") logger.debug("Call to unconnected comm: %s" % self._name) return self._comms_wrapper._register_call(call_dict, self._callback) self._comms_wrapper._send_call(call_dict, self._comm_id, buffers) return self._comms_wrapper._get_call_return_value( call_dict, self._comm_id) spyder-kernels-3.0.3/spyder_kernels/comms/decorators.py000066400000000000000000000012551475072611100233610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) """ Comms decorators. """ def comm_handler(fun): """Decorator to mark comm handler methods.""" fun._is_comm_handler = True return fun def register_comm_handlers(instance, frontend_comm): """ Registers an instance whose methods have been marked with comm_handler. """ for method_name in instance.__class__.__dict__: method = getattr(instance, method_name) if hasattr(method, '_is_comm_handler'): frontend_comm.register_call_handler( method_name, method) spyder-kernels-3.0.3/spyder_kernels/comms/frontendcomm.py000066400000000000000000000164631475072611100237160ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) """ In addition to the remote_call mechanism implemented in CommBase: - Implements _wait_reply, so blocking calls can be made. """ import asyncio import sys import threading import time from IPython.core.getipython import get_ipython import zmq from spyder_kernels.comms.commbase import CommBase, CommError from spyder_kernels.comms.utils import WriteContext def frontend_request(blocking, timeout=None): """ Send a request to the frontend. If blocking is True, The return value will be returned. """ if not get_ipython().kernel.frontend_comm.is_open(): raise CommError("Can't make a request to a closed comm") # Get a reply from the last frontend to have sent a message return get_ipython().kernel.frontend_call( blocking=blocking, broadcast=False, timeout=timeout) class FrontendComm(CommBase): """Mixin to implement the spyder_shell_api.""" def __init__(self, kernel): super(FrontendComm, self).__init__() # Comms self.kernel = kernel self.kernel.comm_manager.register_target( self._comm_name, self._comm_open) self.comm_lock = threading.Lock() self._cached_messages = {} self._pending_comms = {} def close(self, comm_id=None): """Close the comm and notify the other side.""" with self.comm_lock: return super(FrontendComm, self).close(comm_id) def _send_message(self, *args, **kwargs): """Publish custom messages to the other side.""" with self.comm_lock: return super(FrontendComm, self)._send_message(*args, **kwargs) def poll_one(self): """Receive one message from comm socket.""" out_stream = None if self.kernel.shell_streams: # If the message handler needs to send a reply, # use the regular shell stream. out_stream = self.kernel.shell_streams[0] try: ident, msg = self.kernel.session.recv( self.kernel.parent.control_socket, 0) except zmq.error.ContextTerminated: return except Exception: self.kernel.log.warning("Invalid Message:", exc_info=True) return msg_type = msg['header']['msg_type'] handler = self.kernel.control_handlers.get(msg_type, None) if handler is None: self.kernel.log.warning("Unknown message type: %r", msg_type) return try: asyncio.run(handler(out_stream, ident, msg)) except Exception: self.kernel.log.error( "Exception in message handler:", exc_info=True) finally: sys.stdout.flush() sys.stderr.flush() # Flush to ensure reply is sent if out_stream: out_stream.flush(zmq.POLLOUT) def remote_call(self, comm_id=None, blocking=False, callback=None, timeout=None, display_error=False): """Get a handler for remote calls.""" return super(FrontendComm, self).remote_call( blocking=blocking, comm_id=comm_id, callback=callback, timeout=timeout, display_error=display_error) def wait_until(self, condition, timeout=None): """Wait until condition is met. Returns False if timeout.""" if condition(): return True t_start = time.time() while not condition(): if timeout is not None and time.time() > t_start + timeout: return False if threading.current_thread() is self.kernel.parent.control_thread: # Wait for a reply on the comm channel. self.poll_one() else: # Wait 10ms for a reply time.sleep(0.01) return True def cache_message(self, comm_id, msg): """Message from a comm that might be opened later.""" if comm_id not in self._cached_messages: self._cached_messages[comm_id] = [] self._cached_messages[comm_id].append(msg) # --- Private -------- def _check_comm_reply(self): """ Send comm message to frontend to check if the iopub channel is ready """ # Make sure the length doesn't change during iteration pending_comms = list(self._pending_comms.values()) if len(pending_comms) == 0: return for comm in pending_comms: self._notify_comm_ready(comm) self.kernel.io_loop.call_later(1, self._check_comm_reply) def _notify_comm_ready(self, comm): """Send messages about comm readiness to frontend.""" self.remote_call( comm_id=comm.comm_id, callback=self._comm_ready_callback )._comm_ready() def _comm_ready_callback(self, ret): """A comm has replied, so process all cached messages related to it.""" comm = self._pending_comms.pop(self.calling_comm_id, None) if not comm: return # Cached messages for that comm if comm.comm_id in self._cached_messages: for msg in self._cached_messages[comm.comm_id]: comm.handle_msg(msg) self._cached_messages.pop(comm.comm_id) def _wait_reply(self, comm_id, call_id, call_name, timeout, retry=True): """Wait until the frontend replies to a request.""" def reply_received(): """The reply is there!""" return call_id in self._reply_inbox if not self.wait_until(reply_received): if retry: self._wait_reply(comm_id, call_id, call_name, timeout, False) return raise TimeoutError( "Timeout while waiting for '{}' reply.".format( call_name)) def _comm_open(self, comm, msg): """ A new comm is open! """ self.calling_comm_id = comm.comm_id self._register_comm(comm) # IOPub might not be connected yet, keep sending messages until a # reply is received. self._pending_comms[comm.comm_id] = comm self._notify_comm_ready(comm) self.kernel.io_loop.call_later(.3, self._check_comm_reply) def _comm_close(self, msg): """Close comm.""" comm_id = msg['content']['comm_id'] # Send back a close message confirmation # Fixes spyder-ide/spyder#15356 self.close(comm_id) def _async_error(self, error_wrapper): """ Send an async error back to the frontend to be displayed. """ self.remote_call()._async_error(error_wrapper.to_json()) def _register_comm(self, comm): """ Remove side effect ipykernel has. """ def handle_msg(msg): """Handle a comm_msg message""" if comm._msg_callback: comm._msg_callback(msg) comm.handle_msg = handle_msg super(FrontendComm, self)._register_comm(comm) def _remote_callback(self, call_name, call_args, call_kwargs): """Call the callback function for the remote call.""" with WriteContext(call_name): return super(FrontendComm, self)._remote_callback( call_name, call_args, call_kwargs) spyder-kernels-3.0.3/spyder_kernels/comms/utils.py000066400000000000000000000052561475072611100223610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Comms Utilities """ import sys import threading class WriteContext(object): class_lock = threading.RLock() def __init__(self, prefix): self.prefix = prefix def __enter__(self): self.class_lock.acquire() self.files = [sys.stdout, sys.stderr] self.saved_writes = [f.write for f in self.files] thread_id = threading.get_ident() for f in self.files: f.write = WriteWrapper(f.write, self.prefix, thread_id) def __exit__(self, exc_type, exc_value, traceback): try: for f, old_write in zip(self.files, self.saved_writes): f.write = old_write finally: self.class_lock.release() class WriteWrapper(object): """Wrapper to warn user when text is printed.""" def __init__(self, write, name, thread_id): self._write = write self._name = name self._thread_id = thread_id self._warning_shown = False def is_benign_message(self, message): """Determine if a message is benign in order to filter it.""" benign_messages = [ # Fixes spyder-ide/spyder#14928 # Fixes spyder-ide/spyder-kernels#343 'DeprecationWarning', # Fixes spyder-ide/spyder-kernels#365 'IOStream.flush timed out', # Avoid unnecessary messages from set_configuration when changing # Matplotlib options. "Warning: Cannot change to a different GUI toolkit", "%pylab is deprecated", "Populating the interactive namespace", "\n", # Fixes spyder-ide/spyder#21652 "WARNING", "Active device does not have an attribute", ] return any([msg in message for msg in benign_messages]) def __call__(self, string): """Print warning once.""" if self._thread_id != threading.get_ident(): return self._write(string) if not self.is_benign_message(string): if not self._warning_shown: self._warning_shown = True # request_pdb_stop is expected to print messages. if self._name not in ['request_pdb_stop']: self._write( "\nOutput from spyder call " + repr(self._name) + ":\n" ) return self._write(string) spyder-kernels-3.0.3/spyder_kernels/console/000077500000000000000000000000001475072611100211635ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/console/__init__.py000066400000000000000000000005411475072611100232740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Console kernel """ spyder-kernels-3.0.3/spyder_kernels/console/__main__.py000066400000000000000000000017771475072611100232710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- import sys import os if __name__ == '__main__': # Remove the current working directory from sys.path for Python 3.7+ # because since that version it's added by default to sys.path when # using 'python -m'. if sys.version_info[0] == 3 and sys.version_info[1] >= 7: cwd = os.getcwd() if cwd in sys.path: sys.path.remove(cwd) from spyder_kernels.console import start try: start.main() except Exception: # We have to explicitely write to __stderr__ as stderr might already # have been replaced. import traceback traceback.print_exc(file=sys.__stderr__) sys.__stderr__.flush() raise spyder-kernels-3.0.3/spyder_kernels/console/kernel.py000066400000000000000000001160541475072611100230240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Spyder kernel for Jupyter. """ # Standard library imports import faulthandler import json import logging import os import re import sys import traceback import tempfile import threading import cloudpickle # Third-party imports from ipykernel.ipkernel import IPythonKernel from ipykernel import get_connection_info from IPython.core import release as ipython_release from traitlets.config.loader import Config, LazyConfigValue import zmq from zmq.utils.garbage import gc # Local imports import spyder_kernels from spyder_kernels.comms.commbase import stacksummary_to_json from spyder_kernels.comms.frontendcomm import FrontendComm from spyder_kernels.comms.decorators import ( register_comm_handlers, comm_handler) from spyder_kernels.utils.pythonenv import ( get_env_dir, is_conda_env, is_pyenv_env, PythonEnvInfo, PythonEnvType, ) from spyder_kernels.utils.iofuncs import iofunctions from spyder_kernels.utils.mpl import automatic_backend, MPL_BACKENDS_TO_SPYDER from spyder_kernels.utils.nsview import ( get_remote_data, make_remote_view, get_size) from spyder_kernels.utils.style import create_style_class from spyder_kernels.console.shell import SpyderShell from spyder_kernels.comms.utils import WriteContext logger = logging.getLogger(__name__) # Excluded variables from the Variable Explorer (i.e. they are not # shown at all there) EXCLUDED_NAMES = ['In', 'Out', 'exit', 'get_ipython', 'quit'] class SpyderKernel(IPythonKernel): """Spyder kernel for Jupyter.""" shell_class = SpyderShell def __init__(self, *args, **kwargs): super(SpyderKernel, self).__init__(*args, **kwargs) self.comm_manager.get_comm = self._get_comm self.frontend_comm = FrontendComm(self) # All functions that can be called through the comm register_comm_handlers(self, self.frontend_comm) register_comm_handlers(self.shell, self.frontend_comm) self.namespace_view_settings = {} self.faulthandler_handle = None self._cwd_initialised = False # Add handlers to control to process messages while debugging self.control_handlers['comm_msg'] = self.control_comm_msg self.control_handlers['complete_request'] = self.shell_handlers[ 'complete_request'] # Socket to signal shell_stream locally self.loopback_socket = None # To track the interactive backend self.interactive_backend = None # To save the python env info self.pythonenv_info: PythonEnvInfo = {} @property def kernel_info(self): # Used for checking correct version by spyder infos = super().kernel_info infos.update({ "spyder_kernels_info": ( spyder_kernels.__version__, sys.executable ) }) return infos # -- Public API ----------------------------------------------------------- def frontend_call(self, blocking=False, broadcast=True, timeout=None, callback=None, display_error=False): """Call the frontend.""" # If not broadcast, send only to the calling comm if broadcast: comm_id = None else: comm_id = self.frontend_comm.calling_comm_id return self.frontend_comm.remote_call( blocking=blocking, comm_id=comm_id, callback=callback, timeout=timeout, display_error=display_error) def get_state(self): """"get current state to send to the frontend""" state = {} with WriteContext("get_state"): if self._cwd_initialised: state["cwd"] = self.get_cwd() state["namespace_view"] = self.get_namespace_view() state["var_properties"] = self.get_var_properties() return state def publish_state(self): """Publish the current kernel state""" if not self.frontend_comm.is_open(): # No one to send to return try: self.frontend_call(blocking=False).update_state(self.get_state()) except Exception: pass def enable_faulthandler(self): """ Open a file to save the faulthandling and identifiers for internal threads. """ fault_dir = None if sys.platform.startswith('linux'): # Do not use /tmp for temporary files try: from xdg.BaseDirectory import xdg_data_home fault_dir = os.path.join(xdg_data_home, "spyder") os.makedirs(fault_dir, exist_ok=True) except Exception: fault_dir = None self.faulthandler_handle = tempfile.NamedTemporaryFile( 'wt', suffix='.fault', dir=fault_dir ) main_id = threading.main_thread().ident system_ids = [ thread.ident for thread in threading.enumerate() if thread is not threading.main_thread() ] faulthandler.enable(self.faulthandler_handle) return self.faulthandler_handle.name, main_id, system_ids @comm_handler def safe_exec(self, filename): """Safely execute a file using IPKernelApp._exec_file.""" self.parent._exec_file(filename) @comm_handler def get_fault_text(self, fault_filename, main_id, ignore_ids): """Get fault text from old run.""" # Read file try: with open(fault_filename, 'r') as f: fault = f.read() except FileNotFoundError: return except UnicodeDecodeError as e: return ( "Can not read fault file!\n" + "UnicodeDecodeError: " + str(e)) # Remove file try: os.remove(fault_filename) except Exception: pass # Process file if not fault: return thread_regex = ( r"(Current thread|Thread) " r"(0x[\da-f]+) \(most recent call first\):" r"(?:.|\r\n|\r|\n)+?(?=Current thread|Thread|\Z)") # Keep line for future improvements # files_regex = r"File \"([^\"]+)\", line (\d+) in (\S+)" text = "" start_idx = 0 for idx, match in enumerate(re.finditer(thread_regex, fault)): # Add anything non-matched text += fault[start_idx:match.span()[0]] start_idx = match.span()[1] thread_id = int(match.group(2), base=16) if thread_id != main_id: if thread_id in ignore_ids: continue if "wurlitzer.py" in match.group(0): # Wurlitzer threads are launched later continue text += "\n" + match.group(0) + "\n" else: try: pattern = (r".*(?:/IPython/core/interactiveshell\.py|" r"\\IPython\\core\\interactiveshell\.py).*") match_internal = next(re.finditer(pattern, match.group(0))) end_idx = match_internal.span()[0] except StopIteration: end_idx = None text += "\nMain thread:\n" + match.group(0)[:end_idx] + "\n" # Add anything after match text += fault[start_idx:] return text def get_system_threads_id(self): """Return the list of system threads id.""" ignore_threads = [ self.parent.poller, # Parent poller self.shell.history_manager.save_thread, # history self.parent.heartbeat, # heartbeat self.parent.iopub_thread.thread, # iopub gc.thread, # ZMQ garbage collector thread self.parent.control_thread, # control ] return [ thread.ident for thread in ignore_threads if thread is not None] def filter_stack(self, stack, is_main): """Return the part of the stack the user needs to see.""" # Remove wurlitzer frames for frame_summary in stack: if "wurlitzer.py" in frame_summary.filename: return # Cleanup main thread if is_main: start_idx = -1 for idx in range(len(stack)): if stack[idx].filename.endswith( ("IPython/core/interactiveshell.py", "IPython\\core\\interactiveshell.py")): start_idx = idx + 1 if start_idx != -1: stack = stack[start_idx:] else: stack = [] return stack @comm_handler def get_current_frames(self, ignore_internal_threads=True): """Get the current frames.""" ignore_list = self.get_system_threads_id() main_id = threading.main_thread().ident stack_dict = {} thread_names = {thread.ident: thread.name for thread in threading.enumerate()} for thread_id, frame in sys._current_frames().items(): stack = traceback.StackSummary.extract( traceback.walk_stack(frame)) stack.reverse() if ignore_internal_threads: if thread_id in ignore_list: continue stack = self.filter_stack(stack, main_id == thread_id) if stack is not None: if thread_id in thread_names: thread_name = thread_names[thread_id] else: thread_name = str(thread_id) # Transform stack in a dict because FrameSummary objects # are not compatible between versions of Python stack_dict[thread_name] = stacksummary_to_json(stack) return stack_dict # --- For the Variable Explorer @comm_handler def get_namespace_view(self, frame=None): """ Return the namespace view This is a dictionary with the following structure {'a': { 'type': 'str', 'size': 1, 'view': '1', 'python_type': 'int', 'numpy_type': 'Unknown' } } Here: * 'a' is the variable name. * 'type' and 'size' are self-evident. * 'view' is its value or its repr computed with `value_to_display`. * 'python_type' is its Python type computed with `get_type_string`. * 'numpy_type' is its Numpy type (if any) computed with `get_numpy_type_string`. """ settings = self.namespace_view_settings if settings: ns = self.shell._get_current_namespace(frame=frame) view = make_remote_view(ns, settings, EXCLUDED_NAMES) return view else: return None @comm_handler def get_var_properties(self): """ Get some properties of the variables in the current namespace """ settings = self.namespace_view_settings if settings: ns = self.shell._get_current_namespace() data = get_remote_data(ns, settings, mode='editable', more_excluded_names=EXCLUDED_NAMES) properties = {} for name, value in list(data.items()): properties[name] = { 'is_list': self._is_list(value), 'is_dict': self._is_dict(value), 'is_set': self._is_set(value), 'len': self._get_len(value), 'is_array': self._is_array(value), 'is_image': self._is_image(value), 'is_data_frame': self._is_data_frame(value), 'is_series': self._is_series(value), 'array_shape': self._get_array_shape(value), 'array_ndim': self._get_array_ndim(value) } return properties else: return None @comm_handler def get_value(self, name, encoded=False): """Get the value of a variable""" ns = self.shell._get_current_namespace() value = ns[name] if encoded: # Encode with cloudpickle value = cloudpickle.dumps(value) return value @comm_handler def set_value(self, name, value, encoded=False): """Set the value of a variable""" if encoded: # Decode_value value = cloudpickle.loads(value) ns = self.shell._get_reference_namespace(name) ns[name] = value self.log.debug(ns) @comm_handler def remove_value(self, name): """Remove a variable""" ns = self.shell._get_reference_namespace(name) ns.pop(name) @comm_handler def copy_value(self, orig_name, new_name): """Copy a variable""" ns = self.shell._get_reference_namespace(orig_name) ns[new_name] = ns[orig_name] @comm_handler def load_data(self, filename, ext, overwrite=False): """ Load data from filename. Use 'overwrite' to determine if conflicts between variable names need to be handle or not. For example, if a loaded variable is call 'var' and there is already a variable 'var' in the namespace, having 'overwrite=True' will cause 'var' to be updated. In the other hand, with 'overwrite=False', a new variable will be created with a sufix starting with 000 i.e 'var000' (default behavior). """ from spyder_kernels.utils.misc import fix_reference_name glbs = self.shell.user_ns load_func = iofunctions.load_funcs[ext] data, error_message = load_func(filename) if error_message: return error_message if not overwrite: # We convert to list since we mutate this dictionary for key in list(data.keys()): new_key = fix_reference_name(key, blacklist=list(glbs.keys())) if new_key != key: data[new_key] = data.pop(key) try: glbs.update(data) except Exception as error: return str(error) return None @comm_handler def save_namespace(self, filename): """Save namespace into filename""" ns = self.shell._get_current_namespace() settings = self.namespace_view_settings data = get_remote_data(ns, settings, mode='picklable', more_excluded_names=EXCLUDED_NAMES).copy() return iofunctions.save(data, filename) # --- For Pdb def _do_complete(self, code, cursor_pos): """Call parent class do_complete""" return super(SpyderKernel, self).do_complete(code, cursor_pos) def do_complete(self, code, cursor_pos): """ Call PdB complete if we are debugging. Public method of ipykernel overwritten for debugging. """ if self.shell.is_debugging(): return self.shell.pdb_session.do_complete(code, cursor_pos) return self._do_complete(code, cursor_pos) def interrupt_eventloop(self): """ Interrupts the eventloop. To be used when the main thread is blocked by a call to self.eventloop. This can be called from another thread, e.g. the control thread. note: Interrupting the eventloop is only implemented when a message is received on the shell channel, but this message is queued and won't be processed because an `execute` message is being processed. """ if not self.eventloop: return if self.loopback_socket is None: # Add socket to signal shell_stream locally self.loopback_socket = self.shell_stream.socket.context.socket( zmq.DEALER) port = json.loads(get_connection_info())['shell_port'] self.loopback_socket.connect("tcp://127.0.0.1:%i" % port) # Add dummy handler self.shell_handlers["interrupt_eventloop"] = ( lambda stream, ident, parent: None) self.session.send( self.loopback_socket, self.session.msg("interrupt_eventloop")) # --- For the Help plugin @comm_handler def is_defined(self, obj, force_import=False): """Return True if object is defined in current namespace""" from spyder_kernels.utils.dochelpers import isdefined ns = self.shell._get_current_namespace(with_magics=True) return isdefined(obj, force_import=force_import, namespace=ns) @comm_handler def get_doc(self, objtxt): """Get object documentation dictionary""" try: import matplotlib matplotlib.rcParams['docstring.hardcopy'] = True except: pass from spyder_kernels.utils.dochelpers import getdoc obj, valid = self._eval(objtxt) if valid: return getdoc(obj) @comm_handler def get_source(self, objtxt): """Get object source""" from spyder_kernels.utils.dochelpers import getsource obj, valid = self._eval(objtxt) if valid: return getsource(obj) # -- For Matplolib @comm_handler def get_matplotlib_backend(self): """Get current matplotlib backend.""" try: import matplotlib return MPL_BACKENDS_TO_SPYDER[matplotlib.get_backend().lower()] except Exception: return None @comm_handler def get_mpl_interactive_backend(self): """ Get current Matplotlib interactive backend. This is different from the current backend because, for instance, the user can set first the Qt backend, then the Inline one. In that case, the current backend is Inline, but the current interactive one is Qt, and this backend can't be changed without a kernel restart. """ # Backends that Spyder can handle recognized_backends = {'qt', 'tk', 'macosx'} # --- Return backend according to framework if self.interactive_backend is None: # Since no interactive backend has been set yet, this is equivalent # to having the inline one. return 'inline' elif self.interactive_backend in recognized_backends: return self.interactive_backend else: # This covers the case of other backends (e.g. Wx or Gtk) # which users can set interactively with the %matplotlib # magic but not through our Preferences. return -1 @comm_handler def set_matplotlib_conf(self, conf): """Set matplotlib configuration""" pylab_autoload_n = 'pylab/autoload' pylab_backend_n = 'pylab/backend' figure_format_n = 'pylab/inline/figure_format' resolution_n = 'pylab/inline/resolution' width_n = 'pylab/inline/width' height_n = 'pylab/inline/height' fontsize_n = 'pylab/inline/fontsize' bottom_n = 'pylab/inline/bottom' bbox_inches_n = 'pylab/inline/bbox_inches' if figure_format_n in conf: self._set_inline_config_option( 'figure_formats', conf[figure_format_n] ) inline_rc = {} if resolution_n in conf: inline_rc.update({'figure.dpi': conf[resolution_n]}) if width_n in conf or height_n in conf: inline_rc.update( {'figure.figsize': (conf[width_n], conf[height_n])} ) if fontsize_n in conf: inline_rc.update({'font.size': conf[fontsize_n]}) if bottom_n in conf: inline_rc.update({'figure.subplot.bottom': conf[bottom_n]}) # Update Inline backend parameters, if available. if inline_rc: self._set_inline_config_option('rc', inline_rc) if bbox_inches_n in conf: bbox_inches = 'tight' if conf[bbox_inches_n] else None self._set_inline_config_option( 'print_figure_kwargs', {'bbox_inches': bbox_inches} ) # Only update backend if it has changed or if autoloading pylab. pylab_autoload_o = conf.get(pylab_autoload_n, False) current_backend = self.get_matplotlib_backend() pylab_backend_o = conf.get(pylab_backend_n, current_backend) backend_changed = current_backend != pylab_backend_o if pylab_autoload_o or backend_changed: self._set_mpl_backend(pylab_backend_o, pylab_autoload_o) # -- For completions def set_jedi_completer(self, use_jedi): """Enable/Disable jedi as the completer for the kernel.""" self._set_config_option('IPCompleter.use_jedi', use_jedi) def set_greedy_completer(self, use_greedy): """Enable/Disable greedy completer for the kernel.""" self._set_config_option('IPCompleter.greedy', use_greedy) def set_autocall(self, autocall): """Enable/Disable autocall funtionality.""" self._set_config_option('ZMQInteractiveShell.autocall', autocall) # --- Additional methods @comm_handler def set_configuration(self, conf): """Set kernel configuration""" ret = {} for key, value in conf.items(): if key == "cwd": self._cwd_initialised = True os.chdir(value) self.publish_state() elif key == "namespace_view_settings": self.namespace_view_settings = value self.publish_state() elif key == "pdb": self.shell.set_pdb_configuration(value) elif key == "faulthandler": if value: ret[key] = self.enable_faulthandler() elif key == "special_kernel": try: self.set_special_kernel(value) except Exception: ret["special_kernel_error"] = value elif key == "color scheme": self.set_color_scheme(value) elif key == "traceback_highlight_style": # This doesn't work in Python 3.8 because the last IPython # version compatible with it doesn't allow to customize the # syntax highlighting scheme used for tracebacks. # Fixes spyder-ide/spyder#23484 if sys.version_info >= (3, 9): self.set_traceback_syntax_highlighting(value) elif key == "jedi_completer": self.set_jedi_completer(value) elif key == "greedy_completer": self.set_greedy_completer(value) elif key == "autocall": self.set_autocall(value) elif key == "matplotlib": self.set_matplotlib_conf(value) elif key == "update_gui": self.shell.update_gui_frontend = value elif key == "wurlitzer": if value: self._load_wurlitzer() elif key == "autoreload_magic": self._autoreload_magic(value) return ret def set_color_scheme(self, color_scheme): self.shell.set_spyder_theme(color_scheme) self.set_sympy_forecolor(background_color=color_scheme) self.set_traceback_highlighting(color_scheme) def set_traceback_highlighting(self, color_scheme): """Set the traceback highlighting color.""" color = 'bg:ansired' if color_scheme == 'dark' else 'bg:ansiyellow' from IPython.core.ultratb import VerboseTB if getattr(VerboseTB, 'tb_highlight', None) is not None: VerboseTB.tb_highlight = color elif getattr(VerboseTB, '_tb_highlight', None) is not None: VerboseTB._tb_highlight = color def set_traceback_syntax_highlighting(self, syntax_style): """Set the traceback syntax highlighting style.""" import IPython.core.ultratb from IPython.core.ultratb import VerboseTB IPython.core.ultratb.get_style_by_name = create_style_class if getattr(VerboseTB, 'tb_highlight_style', None) is not None: VerboseTB.tb_highlight_style = syntax_style elif getattr(VerboseTB, '_tb_highlight_style', None) is not None: VerboseTB._tb_highlight_style = syntax_style def get_cwd(self): """Get current working directory.""" try: return os.getcwd() except (IOError, OSError): pass @comm_handler def get_syspath(self): """Return sys.path contents.""" return sys.path[:] @comm_handler def get_env(self): """Get environment variables.""" return os.environ.copy() @comm_handler def close_all_mpl_figures(self): """Close all Matplotlib figures.""" try: import matplotlib.pyplot as plt plt.close('all') except: pass def set_special_kernel(self, special): """ Check if optional dependencies are available for special consoles. """ self.shell.special = None if special is None: return if special == "pylab": import matplotlib # noqa exec("from pylab import *", self.shell.user_ns) self.shell.special = special return if special == "sympy": import sympy # noqa sympy_init = "\n".join([ "from sympy import *", "x, y, z, t = symbols('x y z t')", "k, m, n = symbols('k m n', integer=True)", "f, g, h = symbols('f g h', cls=Function)", "init_printing()", ]) exec(sympy_init, self.shell.user_ns) self.shell.special = special return if special == "cython": import cython # noqa # Import pyximport to enable Cython files support for # import statement import pyximport pyx_setup_args = {} # Add Numpy include dir to pyximport/distutils try: import numpy pyx_setup_args['include_dirs'] = numpy.get_include() except Exception: pass # Setup pyximport and enable Cython files reload pyximport.install(setup_args=pyx_setup_args, reload_support=True) self.shell.run_line_magic("reload_ext", "Cython") self.shell.special = special return raise NotImplementedError(f"{special}") @comm_handler def update_syspath(self, path_dict, new_path_dict): """ Update the PYTHONPATH of the kernel. `path_dict` and `new_path_dict` have the paths as keys and the state as values. The state is `True` for active and `False` for inactive. `path_dict` corresponds to the previous state of the PYTHONPATH. `new_path_dict` corresponds to the new state of the PYTHONPATH. """ # Remove old paths for path in path_dict: while path in sys.path: sys.path.remove(path) # Add new paths pypath = [path for path, active in new_path_dict.items() if active] if pypath: sys.path.extend(pypath) os.environ.update({'PYTHONPATH': os.pathsep.join(pypath)}) else: os.environ.pop('PYTHONPATH', None) @comm_handler def get_pythonenv_info(self): """Get the Python env info in which this kernel is installed.""" # We only need to compute this once if not self.pythonenv_info: path = sys.executable.replace("pythonw.exe", "python.exe") if is_conda_env(pyexec=path): env_type = PythonEnvType.Conda elif is_pyenv_env(path): env_type = PythonEnvType.PyEnv else: env_type = PythonEnvType.Custom self.pythonenv_info = PythonEnvInfo( path=path, env_type=env_type, name=get_env_dir(path, only_dir=True), python_version=".".join( [str(n) for n in sys.version_info[:3]] ), # These keys are necessary to build the console banner in # Spyder ipython_version=ipython_release.version, sys_version=sys.version, ) return self.pythonenv_info # -- Private API --------------------------------------------------- # --- For the Variable Explorer def _get_len(self, var): """Return sequence length""" try: return get_size(var) except: return None def _is_array(self, var): """Return True if variable is a NumPy array""" try: import numpy return isinstance(var, numpy.ndarray) except: return False def _is_image(self, var): """Return True if variable is a PIL.Image image""" try: from PIL import Image return isinstance(var, Image.Image) except: return False def _is_data_frame(self, var): """Return True if variable is a DataFrame""" try: from pandas import DataFrame return isinstance(var, DataFrame) except: return False def _is_series(self, var): """Return True if variable is a Series""" try: from pandas import Series return isinstance(var, Series) except: return False def _is_list(self, var): """Return True if variable is a list or tuple.""" # The try/except is necessary to fix spyder-ide/spyder#19516. try: return isinstance(var, (tuple, list)) except Exception: return False def _is_dict(self, var): """Return True if variable is a dictionary.""" # The try/except is necessary to fix spyder-ide/spyder#19516. try: return isinstance(var, dict) except Exception: return False def _is_set(self, var): """Return True if variable is a set.""" # The try/except is necessary to fix spyder-ide/spyder#19516. try: return isinstance(var, set) except Exception: return False def _get_array_shape(self, var): """Return array's shape""" try: if self._is_array(var): return var.shape else: return None except: return None def _get_array_ndim(self, var): """Return array's ndim""" try: if self._is_array(var): return var.ndim else: return None except: return None # --- For the Help plugin def _eval(self, text): """ Evaluate text and return (obj, valid) where *obj* is the object represented by *text* and *valid* is True if object evaluation did not raise any exception """ assert isinstance(text, str) ns = self.shell._get_current_namespace(with_magics=True) try: return eval(text, ns), True except: return None, False # --- For Matplotlib def _set_mpl_backend(self, backend, pylab=False): """ Set a backend for Matplotlib. backend: A parameter that can be passed to %matplotlib (e.g. 'inline' or 'tk'). pylab: Is the pylab magic should be used in order to populate the namespace from numpy and matplotlib """ import traceback # Don't proceed further if there's any error while importing Matplotlib try: import matplotlib except Exception: return generic_error = ( "\n" + "=" * 73 + "\n" "NOTE: The following error appeared when setting " "your Matplotlib backend!!\n" + "=" * 73 + "\n\n" "{0}" ) magic = 'pylab' if pylab else 'matplotlib' if backend == "auto": backend = automatic_backend() error = None try: # This prevents Matplotlib to automatically set the backend, which # overrides our own mechanism. matplotlib.rcParams['backend'] = 'Agg' # Set the backend self.shell.run_line_magic(magic, backend) except RuntimeError as err: # This catches errors generated by ipykernel when # trying to set a backend. See issue 5541 if "GUI eventloops" in str(err): previous_backend = matplotlib.get_backend() if backend not in previous_backend.lower(): # Only inform about an error if the user selected backend # and the one set by Matplotlib are different. Else this # message is very confusing. error = ( "\n" "NOTE: Spyder *can't* set your selected Matplotlib " "backend because there is a previous backend already " "in use.\n\n" "Your backend will be {0}".format(previous_backend) ) # This covers other RuntimeError's else: error = generic_error.format(traceback.format_exc()) except ImportError as err: additional_info = ( "This is most likely caused by missing packages in the Python " "environment\n" "or installation whose interpreter is located at:\n\n" " {0}" ).format(sys.executable) error = generic_error.format(err) + '\n\n' + additional_info except Exception: error = generic_error.format(traceback.format_exc()) if error: print(error) def _set_config_option(self, option, value): """ Set config options using the %config magic. As parameters: option: config option, for example 'InlineBackend.figure_formats'. value: value of the option, for example 'SVG', 'Retina', etc. """ try: base_config = "{option} = " value_line = ( "'{value}'" if isinstance(value, str) else "{value}") config_line = base_config + value_line self.shell.run_line_magic( 'config', config_line.format(option=option, value=value)) except Exception: pass def _set_inline_config_option(self, option, value): """ Update InlineBackend given an option and value. Parameters ---------- option: str Configuration option. One of 'close_figures', 'figure_formats', 'print_figure_kwargs', or 'rc'. value: str | dict Value of the option. """ if ( 'InlineBackend' in self.config and option in self.config['InlineBackend'] and isinstance(value, dict) ): self.config['InlineBackend'][option].update(value) elif 'InlineBackend' in self.config: self.config['InlineBackend'].update({option: value}) else: self.config.update({'InlineBackend': Config({option: value})}) value = self.config['InlineBackend'][option] if isinstance(value, LazyConfigValue): value = value.to_dict().get('update') or value self._set_config_option(f'InlineBackend.{option}', value) if option == 'rc' and self.get_matplotlib_backend() == 'inline': # Explicitly update rcParams if already in inline mode so that # new settings are effective immediately. try: import matplotlib matplotlib.rcParams.update(value) except Exception: pass def restore_rc_file_defaults(self): """Restore inline rcParams to file defaults""" try: import matplotlib except Exception: return if ( 'InlineBackend' in self.config and 'rc' in self.config['InlineBackend'] ): # Only restore keys that may have been set explicitly by # _set_inline_config_option for k in self.config['InlineBackend']['rc'].keys(): matplotlib.rcParams[k] = matplotlib.rcParamsOrig[k] def set_sympy_forecolor(self, background_color='dark'): """Set SymPy forecolor depending on console background.""" if self.shell.special != "sympy": return try: from sympy import init_printing if background_color == 'dark': init_printing(forecolor='White', ip=self.shell) elif background_color == 'light': init_printing(forecolor='Black', ip=self.shell) except Exception: pass # --- Others def _autoreload_magic(self, enable): """Load %autoreload magic.""" try: if enable: self.shell.run_line_magic('reload_ext', 'autoreload') self.shell.run_line_magic('autoreload', "2") else: self.shell.run_line_magic('autoreload', "off") except Exception: pass def _load_wurlitzer(self): """Load wurlitzer extension.""" # Wurlitzer has no effect on Windows if not os.name == 'nt': # Enclose this in a try/except because if it fails the # console will be totally unusable. # Fixes spyder-ide/spyder#8668 try: self.shell.run_line_magic('reload_ext', 'wurlitzer') except Exception: pass def _get_comm(self, comm_id): """ We need to redefine this method from ipykernel.comm_manager to avoid showing a warning when the comm corresponding to comm_id is not present. Fixes spyder-ide/spyder#15498 """ try: return self.comm_manager.comms[comm_id] except KeyError: pass def control_comm_msg(self, stream, ident, msg): """ Handler for comm_msg messages from control channel. If comm is not open yet, cache message. """ content = msg['content'] comm_id = content['comm_id'] comm = self.comm_manager.get_comm(comm_id) if comm is None: self.frontend_comm.cache_message(comm_id, msg) return try: comm.handle_msg(msg) except Exception: self.comm_manager.log.error( 'Exception in comm_msg for %s', comm_id, exc_info=True) def pre_handler_hook(self): """Hook to execute before calling message handler""" pass def post_handler_hook(self): """Hook to execute after calling message handler""" # keep ipykernel behavior of resetting sigint every call self.shell.register_debugger_sigint() # Reset tracing function so that pdb.set_trace works sys.settrace(None) spyder-kernels-3.0.3/spyder_kernels/console/outstream.py000066400000000000000000000050331475072611100235610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Custom Spyder Outstream class. """ import os import sys from ipykernel.iostream import OutStream class TTYOutStream(OutStream): """Subclass of OutStream that represents a TTY.""" def __init__(self, session, pub_thread, name, pipe=None, echo=None, *, watchfd=True): super().__init__(session, pub_thread, name, pipe, echo=echo, watchfd=watchfd, isatty=True) def _flush(self): """This is where the actual send happens. _flush should generally be called in the IO thread, unless the thread has been destroyed (e.g. forked subprocess). NOTE: Overrided method to be able to filter messages. See spyder-ide/spyder#22181 """ self._flush_pending = False self._subprocess_flush_pending = False if self.echo is not None: try: self.echo.flush() except OSError as e: if self.echo is not sys.__stderr__: print(f"Flush failed: {e}", file=sys.__stderr__) for parent, data in self._flush_buffers(): # Messages that will not be printed to the console. This allows us # to deal with issues such as spyder-ide/spyder#22181 filter_messages = ["Parent poll failed."] if data and not any( [message in data for message in filter_messages] ): # FIXME: this disables Session's fork-safe check, # since pub_thread is itself fork-safe. # There should be a better way to do this. self.session.pid = os.getpid() content = {"name": self.name, "text": data} msg = self.session.msg("stream", content, parent=parent) # Each transform either returns a new # message or None. If None is returned, # the message has been 'used' and we return. for hook in self._hooks: msg = hook(msg) if msg is None: return self.session.send( self.pub_thread, msg, ident=self.topic, ) spyder-kernels-3.0.3/spyder_kernels/console/shell.py000066400000000000000000000373641475072611100226610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Spyder shell for Jupyter kernels. """ # Standard library imports import bdb import logging import os import re import signal import sys import traceback from _thread import interrupt_main # Third-party imports from ipykernel.zmqshell import ZMQInteractiveShell # Local imports from spyder_kernels.customize.namespace_manager import NamespaceManager from spyder_kernels.customize.spyderpdb import SpyderPdb from spyder_kernels.customize.code_runner import SpyderCodeRunner from spyder_kernels.comms.commbase import stacksummary_to_json from spyder_kernels.comms.decorators import comm_handler from spyder_kernels.utils.mpl import automatic_backend logger = logging.getLogger(__name__) class SpyderShell(ZMQInteractiveShell): """Spyder shell.""" PDB_CONF_KEYS = [ 'pdb_ignore_lib', 'pdb_execute_events', 'pdb_use_exclamation_mark', 'pdb_stop_first_line', 'breakpoints', 'pdb_publish_stack' ] def __init__(self, *args, **kwargs): # Create _namespace_stack before __init__ self._namespace_stack = [] self._request_pdb_stop = False self.special = None self._pdb_conf = {} super(SpyderShell, self).__init__(*args, **kwargs) self._allow_kbdint = False self.register_debugger_sigint() self.update_gui_frontend = False self._spyder_theme = 'dark' # Substrings of the directory where Spyder-kernels is installed self._package_locations = [ # When the package is properly installed os.path.join("site-packages", "spyder_kernels"), # When it's installed from the external-deps subrepo. We need this # for our tests os.path.join("external-deps", "spyder-kernels", "spyder_kernels") ] # register post_execute self.events.register('post_execute', self.do_post_execute) def init_magics(self): """Init magics""" super().init_magics() self.register_magics(SpyderCodeRunner) def ask_exit(self): """Engage the exit actions.""" if self.active_eventloop not in [None, "inline"]: # Some eventloops prevent the kernel from shutting down self.enable_gui('inline') super().ask_exit() def _showtraceback(self, etype, evalue, stb): """Handle how tracebacks are displayed in the console.""" spyder_stb = [] if etype is bdb.BdbQuit: # Don't show a traceback when exiting our debugger after entering # it through a `breakpoint()` call. This is because calling `!exit` # after `breakpoint()` raises BdbQuit, which throws a long and # useless traceback. spyder_stb.append('') else: # Skip internal frames from the traceback's string representation for line in stb: if ( # Verbose mode re.match(r"File (.*)", line) # Plain mode or re.match(r"\x1b\[(.*) File (.*)", line) ) and ( # The file line should not contain a location where # Spyder-kernels is installed any( [ location in line for location in self._package_locations ] ) ): continue else: spyder_stb.append(line) super()._showtraceback(etype, evalue, spyder_stb) def set_spyder_theme(self, theme): """Set the theme for the console.""" self._spyder_theme = theme if theme == "dark": # Needed to change the colors of tracebacks self.run_line_magic("colors", "linux") elif theme == "light": self.run_line_magic("colors", "lightbg") def get_spyder_theme(self): """Get the theme for the console.""" return self._spyder_theme def enable_matplotlib(self, gui=None): """Enable matplotlib.""" if gui is None or gui.lower() == "auto": gui = automatic_backend() # Before activating the backend, restore to file default those # InlineBackend settings that may have been set explicitly. self.kernel.restore_rc_file_defaults() enabled_gui, backend = super().enable_matplotlib(gui) # This is necessary for IPython 8.24+, which returns None after # enabling the Inline backend. if enabled_gui is None and gui == "inline": enabled_gui = "inline" gui = enabled_gui # Check if the inline backend is registered. It should be at this # point, but sometimes that can fail due to a mismatch between # the installed versions of IPython, matplotlib and matplotlib-inline. # Fixes spyder-ide/spyder#22420. if gui == "inline": is_inline_registered = False # The flush_figures callback should be listed as a post_execute # event if the backend was registered successfully. for event in self.events.callbacks["post_execute"]: if "matplotlib_inline.backend_inline.flush_figures" in repr( event ): is_inline_registered = True break # Manually register the backend in case it wasn't if not is_inline_registered: from IPython.core.pylabtools import activate_matplotlib from matplotlib_inline.backend_inline import ( configure_inline_support ) backend = "module://matplotlib_inline.backend_inline" activate_matplotlib(backend) configure_inline_support(self, backend) # To easily track the current interactive backend if self.kernel.interactive_backend is None: self.kernel.interactive_backend = gui if gui != "inline" else None if self.update_gui_frontend: try: self.kernel.frontend_call( blocking=False ).update_matplotlib_gui(gui) except Exception: pass return gui, backend # --- For Pdb namespace integration def set_pdb_configuration(self, pdb_conf): """ Set Pdb configuration. Parameters ---------- pdb_conf: dict Dictionary containing the configuration. Its keys are part of the `PDB_CONF_KEYS` class constant. """ for key in self.PDB_CONF_KEYS: if key in pdb_conf: self._pdb_conf[key] = pdb_conf[key] if self.pdb_session: setattr(self.pdb_session, key, pdb_conf[key]) def is_debugging(self): """ Check if we are currently debugging. """ for session in self._namespace_stack[::-1]: if isinstance(session, SpyderPdb) and session.curframe is not None: return True return False @property def pdb_session(self): """Get current pdb session.""" for session in self._namespace_stack[::-1]: if isinstance(session, SpyderPdb): return session return None def add_pdb_session(self, pdb_obj): """Add a pdb object to the stack.""" if self.pdb_session == pdb_obj: # Already added return self._namespace_stack.append(pdb_obj) # Set config to pdb obj self.set_pdb_configuration(self._pdb_conf) def remove_pdb_session(self, pdb_obj): """Remove a pdb object from the stack.""" if self.pdb_session != pdb_obj: # Already removed return self._namespace_stack.pop() if self.pdb_session: # Set config to newly active pdb obj self.set_pdb_configuration(self._pdb_conf) def add_namespace_manager(self, ns_manager): """Add namespace manager to stack.""" self._namespace_stack.append(ns_manager) def remove_namespace_manager(self, ns_manager): """Remove namespace manager.""" if self._namespace_stack[-1] != ns_manager: logger.debug("The namespace stack is inconsistent.") return self._namespace_stack.pop() def get_local_scope(self, stack_depth): """ Get local scope at a given frame depth. Needed for magics that use "needs_local_scope" such as timeit """ frame = sys._getframe(stack_depth + 1) return self.context_locals(frame) def context_locals(self, frame=None): """ Get context locals. If frame is not None, make sure frame.f_locals is not registered in a debugger and return frame.f_locals """ for session in self._namespace_stack[::-1]: if isinstance(session, SpyderPdb) and session.curframe is not None: if frame is None or frame == session.curframe: return session.curframe_locals elif frame is None and isinstance(session, NamespaceManager): return session.ns_locals if frame is not None: return frame.f_locals return None @property def _pdb_frame(self): """Return current Pdb frame if there is any""" if self.pdb_session is not None: return self.pdb_session.curframe @property def user_ns(self): """Get the current namespace.""" for session in self._namespace_stack[::-1]: if isinstance(session, SpyderPdb) and session.curframe is not None: # Return first debugging namespace return session.curframe.f_globals elif isinstance(session, NamespaceManager): return session.ns_globals return self.__user_ns @user_ns.setter def user_ns(self, namespace): """Set user_ns.""" self.__user_ns = namespace def _get_current_namespace(self, with_magics=False, frame=None): """Return a copy of the current namespace.""" if frame is not None: ns = frame.f_globals.copy() ns.update(self.context_locals(frame)) return ns ns = {} ns.update(self.user_ns) context_locals = self.context_locals() if context_locals: ns.update(context_locals) # Add magics to ns so we can show help about them on the Help # plugin if with_magics: line_magics = self.magics_manager.magics['line'] cell_magics = self.magics_manager.magics['cell'] ns.update(line_magics) ns.update(cell_magics) return ns def _get_reference_namespace(self, name): """ Return namespace where reference name is defined It returns the user namespace if name has not yet been defined. """ lcls = self.context_locals() if lcls and name in lcls: return lcls return self.user_ns def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None, exception_only=False, running_compiled_code=False): """Display the exception that just occurred.""" super(SpyderShell, self).showtraceback( exc_tuple, filename, tb_offset, exception_only, running_compiled_code) if not exception_only: try: etype, value, tb = self._get_exc_info(exc_tuple) etype = etype.__name__ value = value.args stack = stacksummary_to_json(traceback.extract_tb(tb.tb_next)) self.kernel.frontend_call(blocking=False).show_traceback( etype, value, stack) except Exception: return def register_debugger_sigint(self): """Register sigint handler.""" signal.signal(signal.SIGINT, self.spyderkernel_sigint_handler) @comm_handler def raise_interrupt_signal(self): """Raise interrupt signal.""" if os.name == "nt": # Check if signal handler is callable to avoid # 'int not callable' error (Python issue #23395) if callable(signal.getsignal(signal.SIGINT)): interrupt_main() else: self.kernel.log.error( "Interrupt message not supported on Windows") else: # This is necessary to make the call below work for IPykernel # versions equal or less than 6.21.2 and greater than it. # See ipython/ipykernel#1101 if hasattr(self.kernel, '_send_interupt_children'): self.kernel._send_interupt_children() else: self.kernel._send_interrupt_children() @comm_handler def request_pdb_stop(self): """Request pdb to stop at the next possible position.""" pdb_session = self.pdb_session if pdb_session: if pdb_session.interrupting: # interrupt already requested, wait return # trace_dispatch is active, stop at the next possible position pdb_session.interrupt() elif (self.spyderkernel_sigint_handler == signal.getsignal(signal.SIGINT)): # Use spyderkernel_sigint_handler self._request_pdb_stop = True self.raise_interrupt_signal() else: logger.debug( "Can not signal main thread to stop as SIGINT " "handler was replaced and the debugger is not active. " "The current handler is: " + repr(signal.getsignal(signal.SIGINT)) ) def spyderkernel_sigint_handler(self, signum, frame): """SIGINT handler.""" if self._request_pdb_stop: # SIGINT called from request_pdb_stop self._request_pdb_stop = False debugger = SpyderPdb() debugger.interrupt() debugger.set_trace(frame) return pdb_session = self.pdb_session if pdb_session: # SIGINT called while debugging if pdb_session.allow_kbdint: raise KeyboardInterrupt if pdb_session.interrupting: # second call to interrupt, raise raise KeyboardInterrupt pdb_session.interrupt() return if self._allow_kbdint: # Do not raise KeyboardInterrupt in the middle of ipython code raise KeyboardInterrupt async def run_code(self, *args, **kwargs): """Execute a code object.""" try: try: self._allow_kbdint = True return await super().run_code(*args, **kwargs) finally: self._allow_kbdint = False except KeyboardInterrupt: self.showtraceback() @comm_handler def pdb_input_reply(self, line, echo_stack_entry=True): """Get a pdb command from the frontend.""" debugger = self.pdb_session if not debugger: return debugger._disable_next_stack_entry = not echo_stack_entry debugger._cmd_input_line = line # Interrupts eventloop if needed self.kernel.interrupt_eventloop() def do_post_execute(self): """Flush __std*__ after execution.""" # Flush C standard streams. sys.__stderr__.flush() sys.__stdout__.flush() self.kernel.publish_state() spyder-kernels-3.0.3/spyder_kernels/console/start.py000066400000000000000000000141251475072611100226750ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ File used to start kernels for the IPython Console """ # Standard library imports import os import os.path as osp import sys import site # Third-party imports from traitlets import DottedObjectName # Local imports from spyder_kernels.utils.misc import is_module_installed def import_spydercustomize(): """Import our customizations into the kernel.""" here = osp.dirname(__file__) parent = osp.dirname(here) customize_dir = osp.join(parent, 'customize') # Remove current directory from sys.path to prevent kernel # crashes when people name Python files or modules with # the same name as standard library modules. # See spyder-ide/spyder#8007 while '' in sys.path: sys.path.remove('') # Import our customizations site.addsitedir(customize_dir) import spydercustomize # noqa # Remove our customize path from sys.path try: sys.path.remove(customize_dir) except ValueError: pass def kernel_config(): """Create a config object with IPython kernel options.""" from IPython.core.application import get_ipython_dir from traitlets.config.loader import Config, load_pyconfig_files # ---- IPython config ---- try: profile_path = osp.join(get_ipython_dir(), 'profile_default') cfg = load_pyconfig_files(['ipython_config.py', 'ipython_kernel_config.py'], profile_path) except: cfg = Config() # ---- Spyder config ---- spy_cfg = Config() # Enable/disable certain features for testing testing = os.environ.get('SPY_TESTING') == 'True' if testing: # Don't load nor save history in our IPython consoles. spy_cfg.HistoryAccessor.enabled = False # Jedi completer. jedi_o = os.environ.get('SPY_JEDI_O') == 'True' spy_cfg.IPCompleter.use_jedi = jedi_o # Clear terminal arguments input. # This needs to be done before adding the exec_lines that come from # Spyder, to avoid deleting the sys module if users want to import # it through them. # See spyder-ide/spyder#15788 clear_argv = "import sys; sys.argv = ['']; del sys" spy_cfg.IPKernelApp.exec_lines = [clear_argv] # Prevent other libraries to change the breakpoint builtin. # This started to be a problem since IPykernel 6.3.0. if sys.version_info[0:2] >= (3, 7): spy_cfg.IPKernelApp.exec_lines.append( "import sys; import pdb; " "sys.breakpointhook = pdb.set_trace; " "del sys; del pdb" ) if is_module_installed('matplotlib'): spy_cfg.IPKernelApp.matplotlib = "inline" # Autocall autocall_o = os.environ.get('SPY_AUTOCALL_O') if autocall_o is not None: spy_cfg.ZMQInteractiveShell.autocall = int(autocall_o) # To handle the banner by ourselves spy_cfg.ZMQInteractiveShell.banner1 = '' # Greedy completer greedy_o = os.environ.get('SPY_GREEDY_O') == 'True' spy_cfg.IPCompleter.greedy = greedy_o # Disable the new mechanism to capture and forward low-level output # in IPykernel 6. For that we have Wurlitzer. spy_cfg.IPKernelApp.capture_fd_output = False # Merge IPython and Spyder configs. Spyder prefs will have prevalence # over IPython ones cfg._merge(spy_cfg) return cfg def varexp(line): """ Spyder's variable explorer magic Used to generate plots, histograms and images of the variables displayed on it. """ ip = get_ipython() #analysis:ignore funcname, name = line.split() try: import guiqwt.pyplot as pyplot except: import matplotlib.pyplot as pyplot pyplot.figure(); getattr(pyplot, funcname[2:])(ip._get_current_namespace()[name]) pyplot.show() def main(): # Remove this module's path from sys.path: try: sys.path.remove(osp.dirname(__file__)) except ValueError: pass try: locals().pop('__file__') except KeyError: pass __doc__ = '' __name__ = '__main__' # Import our customizations into the kernel import_spydercustomize() # Remove current directory from sys.path to prevent kernel # crashes when people name Python files or modules with # the same name as standard library modules. # See spyder-ide/spyder#8007 while '' in sys.path: sys.path.remove('') # Main imports from ipykernel.kernelapp import IPKernelApp from spyder_kernels.console.kernel import SpyderKernel class SpyderKernelApp(IPKernelApp): outstream_class = DottedObjectName( 'spyder_kernels.console.outstream.TTYOutStream') def init_pdb(self): """ This method was added in IPykernel 5.3.1 and it replaces the debugger used by the kernel with a new class introduced in IPython 7.15 during kernel's initialization. Therefore, it doesn't allow us to use our debugger. """ pass def close(self): """Close the loopback socket.""" socket = self.kernel.loopback_socket if socket and not socket.closed: socket.close() return super().close() # Fire up the kernel instance. kernel = SpyderKernelApp.instance() kernel.kernel_class = SpyderKernel try: kernel.config = kernel_config() except: pass kernel.initialize() # Set our own magics kernel.shell.register_magic_function(varexp) # Set Pdb class to be used by %debug and %pdb. # This makes IPython consoles to use the class defined in our # sitecustomize instead of their default one. import pdb kernel.shell.InteractiveTB.debugger_cls = pdb.Pdb # Start the (infinite) kernel event loop. kernel.start() if __name__ == '__main__': main() spyder-kernels-3.0.3/spyder_kernels/console/tests/000077500000000000000000000000001475072611100223255ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/console/tests/__init__.py000066400000000000000000000000001475072611100244240ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/console/tests/load_data.npz000066400000000000000000000004161475072611100247670ustar00rootroot00000000000000PK!¤±Ÿªˆˆval1.npyˆˆ“NUMPYv{'descr': ' tic + SETUP_TIMEOUT: # Give up after 5s raise IOError("Kernel failed to write connection file") client.start_channels() client.wait_for_ready() try: yield client finally: client.stop_channels() finally: kernel.terminate() class Comm(): """ Comm base class, copied from qtconsole without the qt stuff """ def __init__(self, target_name, kernel_client, msg_callback=None, close_callback=None): """ Create a new comm. Must call open to use. """ self.target_name = target_name self.kernel_client = kernel_client self.comm_id = uuid.uuid1().hex self._msg_callback = msg_callback self._close_callback = close_callback self._send_channel = self.kernel_client.shell_channel def _send_msg(self, msg_type, content, data, metadata, buffers): """ Send a message on the shell channel. """ if data is None: data = {} if content is None: content = {} content['comm_id'] = self.comm_id content['data'] = data msg = self.kernel_client.session.msg( msg_type, content, metadata=metadata) if buffers: msg['buffers'] = buffers return self._send_channel.send(msg) # methods for sending messages def open(self, data=None, metadata=None, buffers=None): """Open the kernel-side version of this comm""" return self._send_msg( 'comm_open', {'target_name': self.target_name}, data, metadata, buffers) def send(self, data=None, metadata=None, buffers=None): """Send a message to the kernel-side version of this comm""" return self._send_msg( 'comm_msg', {}, data, metadata, buffers) def close(self, data=None, metadata=None, buffers=None): """Close the kernel-side version of this comm""" return self._send_msg( 'comm_close', {}, data, metadata, buffers) def on_msg(self, callback): """Register a callback for comm_msg Will be called with the `data` of any comm_msg messages. Call `on_msg(None)` to disable an existing callback. """ self._msg_callback = callback def on_close(self, callback): """Register a callback for comm_close Will be called with the `data` of the close message. Call `on_close(None)` to disable an existing callback. """ self._close_callback = callback # methods for handling incoming messages def handle_msg(self, msg): """Handle a comm_msg message""" if self._msg_callback: return self._msg_callback(msg) def handle_close(self, msg): """Handle a comm_close message""" if self._close_callback: return self._close_callback(msg) # ============================================================================= # Fixtures # ============================================================================= @pytest.fixture def kernel(request): """Console kernel fixture""" # Get kernel instance kernel = get_kernel() kernel.namespace_view_settings = { 'check_all': False, 'exclude_private': True, 'exclude_uppercase': True, 'exclude_capitalized': False, 'exclude_unsupported': False, 'exclude_callables_and_modules': True, 'excluded_names': [ 'nan', 'inf', 'infty', 'little_endian', 'colorbar_doc', 'typecodes', '__builtins__', '__main__', '__doc__', 'NaN', 'Inf', 'Infinity', 'sctypes', 'rcParams', 'rcParamsDefault', 'sctypeNA', 'typeNA', 'False_', 'True_' ], 'minmax': False, 'filter_on':True } # Teardown def reset_kernel(): asyncio.run(kernel.do_execute('reset -f', True)) request.addfinalizer(reset_kernel) return kernel # ============================================================================= # Tests # ============================================================================= def test_magics(kernel): """Check available magics in the kernel.""" line_magics = kernel.shell.magics_manager.magics['line'] cell_magics = kernel.shell.magics_manager.magics['cell'] for magic in ['alias', 'alias_magic', 'autocall', 'automagic', 'autosave', 'bookmark', 'cd', 'clear', 'colors', 'config', 'connect_info', 'debug', 'dhist', 'dirs', 'doctest_mode', 'ed', 'edit', 'env', 'gui', 'hist', 'history', 'killbgscripts', 'ldir', 'less', 'load', 'load_ext', 'loadpy', 'logoff', 'logon', 'logstart', 'logstate', 'logstop', 'ls', 'lsmagic', 'macro', 'magic', 'matplotlib', 'mkdir', 'more', 'notebook', 'page', 'pastebin', 'pdb', 'pdef', 'pdoc', 'pfile', 'pinfo', 'pinfo2', 'popd', 'pprint', 'precision', 'prun', 'psearch', 'psource', 'pushd', 'pwd', 'pycat', 'pylab', 'qtconsole', 'quickref', 'recall', 'rehashx', 'reload_ext', 'rep', 'rerun', 'reset', 'reset_selective', 'rmdir', 'run', 'save', 'sc', 'set_env', 'sx', 'system', 'tb', 'time', 'timeit', 'unalias', 'unload_ext', 'who', 'who_ls', 'whos', 'xdel', 'xmode']: msg = "magic '%s' is not in line_magics" % magic assert magic in line_magics, msg for magic in ['!', 'HTML', 'SVG', 'bash', 'capture', 'debug', 'file', 'html', 'javascript', 'js', 'latex', 'perl', 'prun', 'pypy', 'python', 'python2', 'python3', 'ruby', 'script', 'sh', 'svg', 'sx', 'system', 'time', 'timeit', 'writefile']: assert magic in cell_magics # --- For the Variable Explorer def test_get_namespace_view(kernel): """ Test the namespace view of the kernel. """ asyncio.run(kernel.do_execute('a = 1', True)) nsview = repr(kernel.get_namespace_view()) assert "'a':" in nsview assert "'type': 'int'" in nsview or "'type': u'int'" in nsview assert "'size': 1" in nsview assert "'view': '1'" in nsview assert "'numpy_type': 'Unknown'" in nsview assert "'python_type': 'int'" in nsview @pytest.mark.parametrize("filter_on", [True, False]) def test_get_namespace_view_filter_on(kernel, filter_on): """ Test the namespace view of the kernel with filters on and off. """ asyncio.run(kernel.do_execute('a = 1', True)) asyncio.run(kernel.do_execute('TestFilterOff = 1', True)) settings = kernel.namespace_view_settings settings['filter_on'] = filter_on settings['exclude_capitalized'] = True nsview = kernel.get_namespace_view() if not filter_on: assert 'a' in nsview assert 'TestFilterOff' in nsview else: assert 'TestFilterOff' not in nsview assert 'a' in nsview # Restore settings for other tests settings['filter_on'] = True settings['exclude_capitalized'] = False def test_get_var_properties(kernel): """ Test the properties fo the variables in the namespace. """ asyncio.run(kernel.do_execute('a = 1', True)) var_properties = repr(kernel.get_var_properties()) assert "'a'" in var_properties assert "'is_list': False" in var_properties assert "'is_dict': False" in var_properties assert "'len': 1" in var_properties assert "'is_array': False" in var_properties assert "'is_image': False" in var_properties assert "'is_data_frame': False" in var_properties assert "'is_series': False" in var_properties assert "'array_shape': None" in var_properties assert "'array_ndim': None" in var_properties def test_get_value(kernel): """Test getting the value of a variable.""" name = 'a' asyncio.run(kernel.do_execute("a = 124", True)) # Check data type send assert kernel.get_value(name) == 124 def test_set_value(kernel): """Test setting the value of a variable.""" name = 'a' asyncio.run(kernel.do_execute('a = 0', True)) value = 10 kernel.set_value(name, value) log_text = get_log_text(kernel) assert "'__builtin__': " "Configuration per file', if you want to capture the " "namespace.\n" ) self.show_global_msg = False if code.rstrip()[-1:] == ";": # Supress output with ; capture_last_expression = False if capture_last_expression: ast_code, capture_last_expression = capture_last_Expr( ast_code, "_spyder_out", ns_globals ) exec_encapsulate_locals( ast_code, ns_globals, ns_locals, exec_fun, filename ) if capture_last_expression: out = ns_globals.pop("_spyder_out", None) if out is not None: return out except SystemExit as status: # ignore exit(0) if status.code: self.shell.showtraceback(exception_only=True) except BaseException as error: if isinstance(error, bdb.BdbQuit) and self.shell.pdb_session: # Ignore BdbQuit if we are debugging, as it is expected. pass elif post_mortem and isinstance(error, Exception): error_type, error, tb = sys.exc_info() self._post_mortem_excepthook(error_type, error, tb) else: # We ignore the call to exec self.shell.showtraceback(tb_offset=1) finally: __tracebackhide__ = "__pdb_exit__" def _count_leading_empty_lines(self, cell): """Count the number of leading empty cells.""" lines = cell.splitlines(keepends=True) if not lines: return 0 for i, line in enumerate(lines): if line and not line.isspace(): return i return len(lines) def _transform_cell(self, code, indent_only=False): """Transform IPython code to Python code.""" number_empty_lines = self._count_leading_empty_lines(code) if indent_only: if not code.endswith("\n"): code += "\n" # Ensure the cell has a trailing newline lines = code.splitlines(keepends=True) lines = leading_indent(leading_empty_lines(lines)) code = "".join(lines) else: tm = TransformerManager() code = tm.transform_cell(code) return "\n" * number_empty_lines + code def _post_mortem_excepthook(self, type, value, tb): """ For post mortem exception handling, print a banner and enable post mortem debugging. """ self.shell.showtraceback((type, value, tb)) p = pdb.Pdb(self.shell.colors) if not type == SyntaxError: # wait for stderr to print (stderr.flush does not work in this case) time.sleep(0.1) print("*" * 40) print("Entering post mortem debugging...") print("*" * 40) # add ability to move between frames p.reset() frame = tb.tb_next.tb_frame # wait for stdout to print time.sleep(0.1) p.interaction(frame, tb) def _parse_argstring(self, magic_func, argstring): """ Parse a string of arguments for a magic function. This is needed because magic_arguments.parse_argstring does platform-dependent things with quotes and backslashes. For example, on Windows, strings are removed and backslashes are escaped. """ argv = shlex.split(argstring) args = magic_func.parser.parse_args(argv) if args.filename is None: args.filename = self._get_current_file_name() args.canonic_filename = canonic(args.filename) return args def _parse_runfile_argstring(self, magic_func, argstring, local_ns): """Parse an args string for runfile and debugfile.""" args = self._parse_argstring(magic_func, argstring) if args.namespace is None: args.namespace = self.shell.user_ns else: if local_ns is not None and args.namespace in local_ns: args.namespace = local_ns[args.namespace] elif args.namespace in self.shell.user_ns: args.namespace = self.shell.user_ns[args.namespace] else: raise NameError( f"name '{args.namespace}' is not defined" ) local_ns = None args.current_namespace = True return args, local_ns def _parse_runcell_argstring(self, magic_func, argstring): """Parse an args string for runcell and debugcell.""" args = self._parse_argstring(magic_func, argstring) args.cell_id = args.name if args.cell_id is None: args.cell_id = int(args.index) return args spyder-kernels-3.0.3/spyder_kernels/customize/namespace_manager.py000077500000000000000000000100541475072611100255460ustar00rootroot00000000000000# # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) import linecache import os.path import types import sys def new_main_mod(filename, modname): """ Reimplemented from IPython/core/interactiveshell.py to avoid caching and clearing recursive namespace. """ filename = os.path.abspath(filename) main_mod = types.ModuleType( modname, doc="Module created for script run in IPython") main_mod.__file__ = filename # It seems pydoc (and perhaps others) needs any module instance to # implement a __nonzero__ method main_mod.__nonzero__ = lambda: True return main_mod class NamespaceManager: """ Get a namespace and set __file__ to filename for this namespace. The namespace is either namespace, the current namespace if current_namespace is True, or a new namespace. """ def __init__( self, shell, filename, current_namespace=False, file_code=None, context_locals=None, context_globals=None, ): self.shell = shell self.filename = filename self.ns_globals = None self.ns_locals = None self.current_namespace = current_namespace self._previous_filename = None self._previous_main = None self._reset_main = False self._file_code = file_code if context_globals is None: context_globals = shell.user_ns self.context_globals = context_globals self.context_locals = context_locals def __enter__(self): """ Prepare the namespace. """ # Save previous __file__ if self.current_namespace: self.ns_globals = self.context_globals self.ns_locals = self.context_locals if '__file__' in self.ns_globals: self._previous_filename = self.ns_globals['__file__'] self.ns_globals['__file__'] = self.filename else: main_mod = new_main_mod(self.filename, '__main__') self.ns_globals = main_mod.__dict__ self.ns_locals = None if '__main__' in sys.modules: self._previous_main = sys.modules['__main__'] sys.modules['__main__'] = main_mod self._reset_main = True # Save current namespace for access by variable explorer self.shell.add_namespace_manager(self) if (self._file_code is not None and isinstance(self._file_code, bytes)): try: self._file_code = self._file_code.decode() except UnicodeDecodeError: # Setting the cache is not supported for non utf-8 files self._file_code = None if self._file_code is not None: # '\n' is used instead of the native line endings. (see linecache) # mtime is set to None to avoid a cache update. linecache.cache[self.filename] = ( len(self._file_code), None, [line + '\n' for line in self._file_code.splitlines()], self.filename) return self.ns_globals, self.ns_locals def __exit__(self, exc_type, exc_val, exc_tb): """ Reset the namespace. """ self.shell.remove_namespace_manager(self) if self._previous_filename: self.ns_globals['__file__'] = self._previous_filename elif '__file__' in self.ns_globals: self.ns_globals.pop('__file__') if not self.current_namespace: if self.context_locals is not None: self.context_locals.update(self.ns_globals) else: self.context_globals.update(self.ns_globals) if self._previous_main: sys.modules['__main__'] = self._previous_main elif '__main__' in sys.modules and self._reset_main: del sys.modules['__main__'] if self.filename in linecache.cache and os.path.exists(self.filename): linecache.cache.pop(self.filename) spyder-kernels-3.0.3/spyder_kernels/customize/spydercustomize.py000066400000000000000000000246711475072611100254000ustar00rootroot00000000000000# # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- # # IMPORTANT NOTE: Don't add a coding line here! It's not necessary for # site files # # Spyder consoles sitecustomize # import os import pdb import sys import warnings from spyder_kernels.customize.spyderpdb import SpyderPdb # ============================================================================= # sys.argv can be missing when Python is embedded, taking care of it. # Fixes Issue 1473 and other crazy crashes with IPython 0.13 trying to # access it. # ============================================================================= if not hasattr(sys, 'argv'): sys.argv = [''] # ============================================================================= # Main constants # ============================================================================= IS_EXT_INTERPRETER = os.environ.get('SPY_EXTERNAL_INTERPRETER') == "True" HIDE_CMD_WINDOWS = os.environ.get('SPY_HIDE_CMD') == "True" # ============================================================================= # Prevent subprocess.Popen calls to create visible console windows on Windows. # See issue #4932 # ============================================================================= if os.name == 'nt' and HIDE_CMD_WINDOWS: import subprocess creation_flag = 0x08000000 # CREATE_NO_WINDOW class SubprocessPopen(subprocess.Popen): def __init__(self, *args, **kwargs): kwargs['creationflags'] = creation_flag super(SubprocessPopen, self).__init__(*args, **kwargs) subprocess.Popen = SubprocessPopen # ============================================================================= # Importing user's sitecustomize # ============================================================================= try: import sitecustomize #analysis:ignore except Exception: pass # ============================================================================= # Set PyQt API to #2 # ============================================================================= if os.environ.get("QT_API") == 'pyqt': try: import sip for qtype in ('QString', 'QVariant', 'QDate', 'QDateTime', 'QTextStream', 'QTime', 'QUrl'): sip.setapi(qtype, 2) except Exception: pass else: try: os.environ.pop('QT_API') except KeyError: pass # ============================================================================= # Patch PyQt4 and PyQt5 # ============================================================================= # This saves the QApplication instances so that Python doesn't destroy them. # Python sees all the QApplication as differnet Python objects, while # Qt sees them as a singleton (There is only one Application!). Deleting one # QApplication causes all the other Python instances to become broken. # See spyder-ide/spyder/issues/2970 try: from PyQt5 import QtWidgets class SpyderQApplication(QtWidgets.QApplication): def __init__(self, *args, **kwargs): super(SpyderQApplication, self).__init__(*args, **kwargs) # Add reference to avoid destruction # This creates a Memory leak but avoids a Segmentation fault SpyderQApplication._instance_list.append(self) SpyderQApplication._instance_list = [] QtWidgets.QApplication = SpyderQApplication except Exception: pass try: from PyQt4 import QtGui class SpyderQApplication(QtGui.QApplication): def __init__(self, *args, **kwargs): super(SpyderQApplication, self).__init__(*args, **kwargs) # Add reference to avoid destruction # This creates a Memory leak but avoids a Segmentation fault SpyderQApplication._instance_list.append(self) SpyderQApplication._instance_list = [] QtGui.QApplication = SpyderQApplication except Exception: pass # ============================================================================= # IPython adjustments # ============================================================================= # Patch unittest.main so that errors are printed directly in the console. # See http://comments.gmane.org/gmane.comp.python.ipython.devel/10557 # Fixes Issue 1370 import unittest from unittest import TestProgram class IPyTesProgram(TestProgram): def __init__(self, *args, **kwargs): test_runner = unittest.TextTestRunner(stream=sys.stderr) kwargs['testRunner'] = kwargs.pop('testRunner', test_runner) kwargs['exit'] = False TestProgram.__init__(self, *args, **kwargs) unittest.main = IPyTesProgram # Ignore some IPython/ipykernel warnings try: warnings.filterwarnings(action='ignore', category=DeprecationWarning, module='ipykernel.ipkernel') except Exception: pass # ============================================================================= # Turtle adjustments # ============================================================================= # This is needed to prevent turtle scripts crashes after multiple runs in the # same IPython Console instance. # See Spyder issue #6278 try: import turtle from turtle import Screen, Terminator def spyder_bye(): try: Screen().bye() turtle.TurtleScreen._RUNNING = True except Terminator: pass turtle.bye = spyder_bye except Exception: pass # ============================================================================= # Pandas adjustments # ============================================================================= try: import pandas as pd # Set Pandas output encoding pd.options.display.encoding = 'utf-8' # Filter warning that appears for DataFrames with np.nan values # Example: # >>> import pandas as pd, numpy as np # >>> pd.Series([np.nan,np.nan,np.nan],index=[1,2,3]) # Fixes Issue 2991 # For 0.18- warnings.filterwarnings(action='ignore', category=RuntimeWarning, module='pandas.core.format', message=".*invalid value encountered in.*") # For 0.18.1+ warnings.filterwarnings(action='ignore', category=RuntimeWarning, module='pandas.formats.format', message=".*invalid value encountered in.*") except Exception: pass # ============================================================================= # Numpy adjustments # ============================================================================= try: # Filter warning that appears when users have 'Show max/min' # turned on and Numpy arrays contain a nan value. # Fixes Issue 7063 # Note: It only happens in Numpy 1.14+ warnings.filterwarnings(action='ignore', category=RuntimeWarning, module='numpy.core._methods', message=".*invalid value encountered in.*") except Exception: pass # ============================================================================= # Multiprocessing adjustments # ============================================================================= # This could fail with changes in Python itself, so we protect it # with a try/except try: import multiprocessing.spawn _old_preparation_data = multiprocessing.spawn.get_preparation_data def _patched_preparation_data(name): """ Patched get_preparation_data to work when all variables are removed before execution. """ try: d = _old_preparation_data(name) except AttributeError: main_module = sys.modules['__main__'] # Any string for __spec__ does the job main_module.__spec__ = '' d = _old_preparation_data(name) # On windows, there is no fork, so we need to save the main file # and import it if (os.name == 'nt' and 'init_main_from_path' in d and not os.path.exists(d['init_main_from_path'])): print( "Warning: multiprocessing may need the main file to exist. " "Please save {}".format(d['init_main_from_path'])) # Remove path as the subprocess can't do anything with it del d['init_main_from_path'] return d multiprocessing.spawn.get_preparation_data = _patched_preparation_data except Exception: pass # ============================================================================= # OS adjustments # ============================================================================= # This is necessary to have better support for Rich and Colorama. def _patched_get_terminal_size(fd=None): return os.terminal_size((80, 30)) os.get_terminal_size = _patched_get_terminal_size # ============================================================================= # Pdb adjustments # ============================================================================= pdb.Pdb = SpyderPdb # ============================================================================= # Remove TMPDIR env var in case it was set by Spyder # ============================================================================= # See spyder-ide/spyder#22382 and spyder-ide/spyder#22394 for the details. def restore_tmpdir(): # This check is necessary for external/remote kernels because SPY_TMPDIR # is not available for them. if os.environ.get("SPY_TMPDIR") is not None: spy_tmpdir = os.environ.get("SPY_TMPDIR") if spy_tmpdir != "": # This means TMPDIR was available in the system when the kernel # started, so we need to restore it. os.environ.update({"TMPDIR": spy_tmpdir}) else: # Otherwise, we simply need to remove it try: os.environ.pop("TMPDIR") except KeyError: pass restore_tmpdir() # ============================================================================= # PYTHONPATH and sys.path Adjustments # ============================================================================= # PYTHONPATH is not passed to kernel directly, see spyder-ide/spyder#13519 # This allows the kernel to start without crashing if modules in PYTHONPATH # shadow standard library modules. def set_spyder_pythonpath(): pypath = os.environ.get('SPY_PYTHONPATH') if pypath: sys.path.extend(pypath.split(os.pathsep)) os.environ.update({'PYTHONPATH': pypath}) set_spyder_pythonpath() spyder-kernels-3.0.3/spyder_kernels/customize/spyderpdb.py000077500000000000000000000756651475072611100241370ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) """Spyder debugger.""" import ast import bdb import builtins from contextlib import contextmanager import logging import os import sys import traceback import threading from collections import namedtuple from functools import lru_cache from IPython.core.autocall import ZMQExitAutocall from IPython.core.debugger import Pdb as ipyPdb from IPython.core.inputtransformer2 import TransformerManager import spyder_kernels from spyder_kernels.comms.commbase import stacksummary_to_json from spyder_kernels.comms.frontendcomm import CommError, frontend_request from spyder_kernels.customize.utils import ( path_is_library, capture_last_Expr, exec_encapsulate_locals ) logger = logging.getLogger(__name__) class DebugWrapper: """ Notifies the frontend when debugging starts/stops """ def __init__(self, pdb_obj): self.pdb_obj = pdb_obj self._cleanup = True def __enter__(self): """ Debugging starts. """ shell = self.pdb_obj.shell if shell.pdb_session == self.pdb_obj: self._cleanup = False else: shell.add_pdb_session(self.pdb_obj) self._cleanup = True def __exit__(self, exc_type, exc_val, exc_tb): """ Debugging ends. """ if self._cleanup: self.pdb_obj.shell.remove_pdb_session(self.pdb_obj) class SpyderPdb(ipyPdb): """ Extends Pdb to add features: - Process IPython magics. - Accepts multiline input. - Better interrupt signal handling. - Option to skip libraries while stepping. - Add completion to non-command code. """ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False): """Init Pdb.""" self.curframe_locals = None # Only set to true when calling debugfile self.continue_if_has_breakpoints = False self.pdb_ignore_lib = False self.pdb_execute_events = False self.pdb_use_exclamation_mark = False self.pdb_publish_stack = False self._exclamation_warning_printed = False self.pdb_stop_first_line = True self._disable_next_stack_entry = False super(SpyderPdb, self).__init__() # content of tuple: (filename, line number) self._previous_step = None # Don't report hidden frames for IPython 7.24+. This attribute # has no effect in previous versions. self.report_skipped = False # Keep track of remote filename self.remote_filename = None # Needed to know which namespace to show (user or current frame) # Line received from the frontend self._cmd_input_line = None # Disable sigint so we can do it ourselves self.nosigint = True # Keep track of interrupting state to avoid several interruptions self.interrupting = False # Should the frontend force go to the current line? self._request_where = False # Turn off IPython's debugger skip funcionality by default because # it makes our debugger quite slow. It's also important to remark # that this functionality doesn't do anything on its own. Users # need to mark what frames they want to skip for it to be useful. # So, we hope that knowledgeable users will find that they need to # enable it in Spyder. # Fixes spyder-ide/spyder#20639. self._predicates["debuggerskip"] = False # Save seen files inodes self._canonic_inode_to_filename = {} self._canonic_filename_to_inode = {} # --- Methods overriden for code execution def print_exclamation_warning(self): """Print pdb warning for exclamation mark.""" if not self._exclamation_warning_printed: print("Warning: The exclamation mark option is enabled. " "Please use '!' as a prefix for Pdb commands.") self._exclamation_warning_printed = True def default(self, line): """ Default way of running pdb statment. """ execute_events = self.pdb_execute_events if line[:1] == '!': line = line[1:] elif self.pdb_use_exclamation_mark: self.print_exclamation_warning() self.error("Unknown command '" + line.split()[0] + "'") return # Replace %debug magic in the debugger if line.startswith("%debug") or line.startswith("%%debug"): cmd, arg, _ = self.parseline(line.lstrip("%")) if cmd == "debug": return self.do_debug(arg) local_ns = self.curframe_locals global_ns = self.curframe.f_globals if self.pdb_use_exclamation_mark: # Find pdb commands executed without ! cmd, arg, line = self.parseline(line) if cmd: cmd_in_namespace = ( cmd in global_ns or cmd in local_ns or cmd in builtins.__dict__ ) # Special case for quit and exit if cmd in ("quit", "exit"): if cmd in global_ns and isinstance( global_ns[cmd], ZMQExitAutocall): # Use the pdb call cmd_in_namespace = False cmd_func = getattr(self, 'do_' + cmd, None) is_pdb_cmd = cmd_func is not None # Look for assignment is_assignment = False try: for node in ast.walk(ast.parse(line)): if isinstance(node, ast.Assign): is_assignment = True break except SyntaxError: pass if is_pdb_cmd: if not cmd_in_namespace and not is_assignment: # This is a pdb command without the '!' prefix. self.lastcmd = line return cmd_func(arg) else: # The pdb command is masked by something self.print_exclamation_warning() try: is_magic = line.startswith("%") line = TransformerManager().transform_cell(line) save_stdout = sys.stdout save_stdin = sys.stdin save_displayhook = sys.displayhook try: sys.stdin = self.stdin sys.stdout = self.stdout sys.displayhook = self.displayhook if execute_events: self.shell.events.trigger('pre_execute') code_ast = ast.parse(line) if line.rstrip()[-1:] == ";": # Supress output with ; capture_last_expression = False else: code_ast, capture_last_expression = capture_last_Expr( code_ast, "_spyderpdb_out", global_ns) if is_magic: # Magics like runcell use and modify local_ns. # But the locals() dict can not be directly modified when # encapsulated. Therefore they must encapsulate the locals # themselves (see code_runner.py). exec(compile(code_ast, "", "exec"), global_ns, local_ns) else: exec_encapsulate_locals(code_ast, global_ns, local_ns) if capture_last_expression: out = global_ns.pop("_spyderpdb_out", None) if out is not None: sys.stdout.flush() sys.stderr.flush() try: frontend_request(blocking=False).show_pdb_output( repr(out)) except (CommError, TimeoutError): # Fallback print("pdb out> ", repr(out)) finally: if execute_events: self.shell.events.trigger('post_execute') sys.stdout = save_stdout sys.stdin = save_stdin sys.displayhook = save_displayhook except BaseException: exc_info = sys.exc_info()[:2] self.error( traceback.format_exception_only(*exc_info)[-1].strip()) # --- Methods overriden for signal handling def interrupt(self): """Stop debugger on next instruction.""" self.interrupting = True self.message("\nProgram interrupted. (Use 'cont' to resume).") self.set_step() def set_trace(self, frame=None): """Register that debugger is tracing.""" self.shell.add_pdb_session(self) super(SpyderPdb, self).set_trace(frame) def set_quit(self): """Register that debugger is not tracing.""" self.shell.remove_pdb_session(self) super(SpyderPdb, self).set_quit() def interaction(self, frame, traceback): """ Called when a user interaction is required. """ with DebugWrapper(self): # Wrapp in case the frontend was not notified, e.g. postmortem return super(SpyderPdb, self).interaction( frame, traceback) def print_stack_entry(self, *args, **kwargs): """Disable printing stack entry if requested.""" if self._disable_next_stack_entry: self._disable_next_stack_entry = False return return super().print_stack_entry(*args, **kwargs) # --- Methods overriden for skipping libraries def stop_here(self, frame): """Check if pdb should stop here.""" # Never stop if we are continuing unless there is a breakpoint if self.stopframe == self.botframe and self.stoplineno == -1: return False if self.continue_if_has_breakpoints and self.should_continue(frame): self.set_continue() return False if ( frame is not None and "__tracebackhide__" in frame.f_locals and frame.f_locals["__tracebackhide__"] == "__pdb_exit__" ): self.onecmd('exit') return False if not super().stop_here(frame): return False if frame is self.stopframe: return True filename = frame.f_code.co_filename if filename.startswith('<'): # This is not a file return True if self.pdb_ignore_lib and path_is_library(filename): return False if self.skip_hidden and os.path.dirname(spyder_kernels.__file__) in filename: # This is spyder-kernels internals return False return True def should_continue(self, frame): """ Jump to first breakpoint if needed. Fixes spyder-ide/spyder#2034 """ if not self.continue_if_has_breakpoints: # This was disabled return False self.continue_if_has_breakpoints = False # Get all breakpoints for the file we're going to debug if not frame: # We are not debugging, return. Solves spyder-ide/spyder#10290 return False lineno = frame.f_lineno breaks = self.get_file_breaks(frame.f_code.co_filename) # Do 'continue' if the first breakpoint is *not* placed # where the debugger is going to land. # Fixes spyder-ide/spyder#4681 if self.pdb_stop_first_line: return breaks and lineno < breaks[0] # The breakpoint could be in another file. return not (breaks and lineno >= breaks[0]) def do_where(self, arg): """w(here) Print a stack trace, with the most recent frame at the bottom. An arrow indicates the "current frame", which determines the context of most commands. 'bt' is an alias for this command. Take a number as argument as an (optional) number of context line to print""" self._request_where = True return super(SpyderPdb, self).do_where(arg) do_w = do_where do_bt = do_where # --- Method defined by us to respond to ipython complete protocol def do_complete(self, code, cursor_pos): """ Respond to a complete request. """ if self.pdb_use_exclamation_mark: return self._complete_exclamation(code, cursor_pos) else: return self._complete_default(code, cursor_pos) def _complete_default(self, code, cursor_pos): """ Respond to a complete request if not pdb_use_exclamation_mark. """ if cursor_pos is None: cursor_pos = len(code) # Get text to complete text = code[:cursor_pos].split(' ')[-1] # Choose Pdb function to complete, based on cmd.py origline = code line = origline.lstrip() if not line: # Nothing to complete return stripped = len(origline) - len(line) begidx = cursor_pos - len(text) - stripped endidx = cursor_pos - stripped compfunc = None ipython_do_complete = True if begidx > 0: # This could be after a Pdb command cmd, args, _ = self.parseline(line) if cmd != '': try: # Function to complete Pdb command arguments compfunc = getattr(self, 'complete_' + cmd) # Don't call ipython do_complete for commands ipython_do_complete = False except AttributeError: pass elif line[0] != '!': # This could be a Pdb command compfunc = self.completenames def is_name_or_composed(text): if not text or text[0] == '.': return False # We want to keep value.subvalue return text.replace('.', '').isidentifier() while text and not is_name_or_composed(text): text = text[1:] begidx += 1 matches = [] if compfunc: matches = compfunc(text, line, begidx, endidx) cursor_start = cursor_pos - len(text) if ipython_do_complete: # Make complete call with current frame if self.curframe: if self.curframe_locals: Frame = namedtuple("Frame", ["f_locals", "f_globals"]) frame = Frame(self.curframe_locals, self.curframe.f_globals) else: frame = self.curframe self.shell.set_completer_frame(frame) result = self.shell.kernel._do_complete(code, cursor_pos) # Reset frame self.shell.set_completer_frame() # If there is no Pdb results to merge, return the result if not compfunc: return result ipy_matches = result['matches'] # Make sure both match lists start at the same place if cursor_start < result['cursor_start']: # Fill IPython matches missing_txt = code[cursor_start:result['cursor_start']] ipy_matches = [missing_txt + m for m in ipy_matches] elif result['cursor_start'] < cursor_start: # Fill Pdb matches missing_txt = code[result['cursor_start']:cursor_start] matches = [missing_txt + m for m in matches] cursor_start = result['cursor_start'] # Add Pdb-specific matches matches += [match for match in ipy_matches if match not in matches] return {'matches': matches, 'cursor_end': cursor_pos, 'cursor_start': cursor_start, 'metadata': {}, 'status': 'ok'} def _complete_exclamation(self, code, cursor_pos): """ Respond to a complete request if pdb_use_exclamation_mark. """ if cursor_pos is None: cursor_pos = len(code) # Get text to complete text = code[:cursor_pos].split(' ')[-1] # Choose Pdb function to complete, based on cmd.py origline = code line = origline.lstrip() if not line: # Nothing to complete return is_pdb_command = line[0] == '!' is_pdb_command_name = False stripped = len(origline) - len(line) begidx = cursor_pos - len(text) - stripped endidx = cursor_pos - stripped compfunc = None if is_pdb_command: line = line[1:] begidx -= 1 endidx -= 1 if begidx == -1: is_pdb_command_name = True text = text[1:] begidx += 1 compfunc = self.completenames else: cmd, args, _ = self.parseline(line) if cmd != '': try: # Function to complete Pdb command arguments compfunc = getattr(self, 'complete_' + cmd) except AttributeError: # This command doesn't exist, nothing to complete return else: # We don't know this command return if not is_pdb_command_name: # Remove eg. leading opening parenthesis def is_name_or_composed(text): if not text or text[0] == '.': return False # We want to keep value.subvalue return text.replace('.', '').isidentifier() while text and not is_name_or_composed(text): text = text[1:] begidx += 1 cursor_start = cursor_pos - len(text) matches = [] if is_pdb_command: matches = compfunc(text, line, begidx, endidx) return { 'matches': matches, 'cursor_end': cursor_pos, 'cursor_start': cursor_start, 'metadata': {}, 'status': 'ok' } # Make complete call with current frame if self.curframe: if self.curframe_locals: Frame = namedtuple("Frame", ["f_locals", "f_globals"]) frame = Frame(self.curframe_locals, self.curframe.f_globals) else: frame = self.curframe self.shell.set_completer_frame(frame) result = self.shell.kernel._do_complete(code, cursor_pos) # Reset frame self.shell.set_completer_frame() return result # --- Methods overriden by us for Spyder integration def postloop(self): # postloop() is called when the debugger’s input prompt exists. Reset # _previous_step so that get_pdb_state() actually notifies Spyder # about a changed frame the next the input prompt is entered again. self._previous_step = None def set_continue(self): """ Stop only at breakpoints or when finished. Reimplemented to avoid stepping out of debugging if there are no breakpoints. We could add more later. """ # Don't stop except at breakpoints or when finished self._set_stopinfo(self.botframe, None, -1) def do_debug(self, arg): """ Debug code Enter a recursive debugger that steps through the code argument (which is an arbitrary expression or statement to be executed in the current environment). """ with self.recursive_debugger() as debugger: self.message("Entering recursive debugger") try: global_ns = self.curframe.f_globals local_ns = self.curframe_locals return sys.call_tracing(debugger.run, (arg, global_ns, local_ns)) except Exception: exc_info = sys.exc_info()[:2] self.error( traceback.format_exception_only(*exc_info)[-1].strip()) finally: self.message("Leaving recursive debugger") @contextmanager def recursive_debugger(self): """Get a recursive debugger.""" # Save and restore tracing function trace_function = sys.gettrace() sys.settrace(None) # Create child debugger debugger = self.__class__( completekey=self.completekey, stdin=self.stdin, stdout=self.stdout) debugger.prompt = "(%s) " % self.prompt.strip() try: yield debugger finally: # Reset parent debugger sys.settrace(trace_function) self.lastcmd = debugger.lastcmd # Reset _previous_step so that get_pdb_state() notifies Spyder about # a changed debugger position. The reset is required because the # recursive debugger might change the position, but the parent # debugger (self) is not aware of this. self._previous_step = None def user_return(self, frame, return_value): """This function is called when a return trap is set here.""" # This is useful when debugging in an active interpreter (otherwise, # the debugger will stop before reaching the target file) if self._wait_for_mainpyfile: if (self.mainpyfile != self.canonic(frame.f_code.co_filename) or frame.f_lineno <= 0): return self._wait_for_mainpyfile = False super(SpyderPdb, self).user_return(frame, return_value) def _cmdloop(self): """Modifies the error text.""" self.interrupting = False while True: try: # keyboard interrupts allow for an easy way to cancel # the current command, so allow them during interactive input self.allow_kbdint = True self.cmdloop() self.allow_kbdint = False break except KeyboardInterrupt: print("--KeyboardInterrupt--\n" "For copying text while debugging, use Ctrl+Shift+C", file=self.stdout) @lru_cache def canonic(self, filename): """ Return canonical form of filename. In some case two path can point to the same file. For this reason os.path.samefile uses os.stat. Here we normalise the path with os.stat so a single path is returned for the same file. see: https://docs.python.org/3/library/os.path.html#os.path.samefile note: os.stat can be slow on windows so call it once per file. """ if filename == "<" + filename[1:-1] + ">": return filename filename = super().canonic(filename) if filename in self._canonic_filename_to_inode: inode = self._canonic_filename_to_inode[filename] else: try: stat = os.stat(filename) except OSError: self._canonic_filename_to_inode[filename] = None return filename inode = (stat.st_dev, stat.st_ino) if stat.st_ino == 0: inode = None self._canonic_filename_to_inode[filename] = inode if inode is not None and inode not in self._canonic_inode_to_filename: # First time this inode is seen self._canonic_inode_to_filename[inode] = filename if inode is None: return filename return self._canonic_inode_to_filename[inode] def do_exitdb(self, arg): """Exit the debugger""" self._set_stopinfo(self.botframe, None, -1) sys.settrace(None) frame = sys._getframe().f_back while frame and frame is not self.botframe: del frame.f_trace frame = frame.f_back return 1 def cmdloop(self, intro=None): """ Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. """ self.preloop() if intro is not None: self.intro = intro if self.intro: self.stdout.write(str(self.intro)+"\n") stop = None while not stop: if self.cmdqueue: # Anything available in cmdqueue is a Pdb command, so we need # to process it as such. # Fixes spyder-ide/spyder#22500 line = ( "!" if self.pdb_use_exclamation_mark else "" ) + self.cmdqueue.pop(0) else: try: line = self.cmd_input(self.prompt) except EOFError: line = 'EOF' line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) self.postloop() def cmd_input(self, prompt=''): """ Get input from frontend. Blocks until return """ kernel = self.shell.kernel # Only works if the comm is open if not kernel.frontend_comm.is_open(): return input(prompt) # Flush output before making the request. sys.stderr.flush() sys.stdout.flush() sys.__stderr__.flush() sys.__stdout__.flush() # Send the input request. self._cmd_input_line = None kernel.frontend_call(display_error=True).pdb_input( prompt, state=self.get_pdb_state()) # Allow GUI event loop to update is_main_thread = ( threading.current_thread() is threading.main_thread()) # Get input by running eventloop if is_main_thread and kernel.eventloop: while self._cmd_input_line is None: eventloop = kernel.eventloop # Check if the current backend is Tk on Windows # to let GUI update. # See spyder-ide/spyder#17523 if (eventloop and hasattr(kernel, "app_wrapper") and os.name == "nt"): kernel.app_wrapper.app.update() elif eventloop: eventloop(kernel) else: break # Get input by blocking if self._cmd_input_line is None: kernel.frontend_comm.wait_until( lambda: self._cmd_input_line is not None) return self._cmd_input_line def precmd(self, line): """ Hook method executed just before the command line is interpreted, but after the input prompt is generated and issued. Here we switch ! and non ! """ if not self.pdb_use_exclamation_mark: return line if not line: return line if line[0] == '!': line = line[1:] else: line = '!' + line return line def postcmd(self, stop, line): """Hook method executed just after a command dispatch is finished.""" # Flush in case the command produced output on underlying outputs sys.__stderr__.flush() sys.__stdout__.flush() return stop # --- Methods defined by us for Spyder integration def set_spyder_breakpoints(self, breakpoints): """Set Spyder breakpoints.""" self.clear_all_breaks() # -----Really deleting all breakpoints: for bp in bdb.Breakpoint.bpbynumber: if bp: bp.deleteMe() bdb.Breakpoint.next = 1 bdb.Breakpoint.bplist = {} bdb.Breakpoint.bpbynumber = [None] # ----- for fname, data in list(breakpoints.items()): for linenumber, condition in data: try: self.set_break(self.canonic(fname), linenumber, cond=condition) except ValueError: # Fixes spyder/issues/15546 # The file is not readable pass breakpoints = property(fset=set_spyder_breakpoints) def get_pdb_state(self): """ Send debugger state (frame position) to the frontend. The state is only sent if it has changed since the last update. """ state = self.shell.kernel.get_state() frame = self.curframe if frame is None: self._previous_step = None return state if self._request_where: self._request_where = False state["do_where"] = True # Get filename and line number of the current frame fname = self.canonic(frame.f_code.co_filename) if fname == self.mainpyfile and self.remote_filename is not None: fname = self.remote_filename lineno = frame.f_lineno if self._previous_step == (fname, lineno): # Do not update state if not needed return state # Set step of the current frame (if any) step = {} self._previous_step = None if isinstance(fname, str) and isinstance(lineno, int): step = dict(fname=fname, lineno=lineno) self._previous_step = (fname, lineno) state['step'] = step if self.pdb_publish_stack: # Publish Pdb stack so we can update the Debugger plugin on Spyder pdb_stack = stacksummary_to_json( traceback.StackSummary.extract(self.stack) ) pdb_index = self.curindex skip_hidden = getattr(self, 'skip_hidden', False) if skip_hidden: # Filter out the hidden frames hidden = self.hidden_frames(self.stack) pdb_stack = [f for f, h in zip(pdb_stack, hidden) if not h] # Adjust the index pdb_index -= sum([bool(i) for i in hidden[:pdb_index]]) state['stack'] = (pdb_stack, pdb_index) return state def run(self, cmd, globals=None, locals=None): """Debug a statement executed via the exec() function. globals defaults to __main__.dict; locals defaults to globals. """ with DebugWrapper(self): super(SpyderPdb, self).run(cmd, globals, locals) def runeval(self, expr, globals=None, locals=None): """Debug an expression executed via the eval() function. globals defaults to __main__.dict; locals defaults to globals. """ with DebugWrapper(self): super(SpyderPdb, self).runeval(expr, globals, locals) def runcall(self, *args, **kwds): """Debug a single function call. Return the result of the function call. """ with DebugWrapper(self): super(SpyderPdb, self).runcall(*args, **kwds) def set_remote_filename(self, filename): """Set remote filename to signal Spyder on mainpyfile.""" self.remote_filename = filename self.mainpyfile = self.canonic(filename) self._wait_for_mainpyfile = True spyder-kernels-3.0.3/spyder_kernels/customize/tests/000077500000000000000000000000001475072611100227055ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/customize/tests/__init__.py000066400000000000000000000005531475072611100250210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Tests for spydercustomize.""" spyder-kernels-3.0.3/spyder_kernels/customize/tests/test_umr.py000066400000000000000000000047301475072611100251250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Tests for the User Module Reloader.""" # Stdlib imports import os import sys # Third party imports import pytest # Local imports from spyder_kernels.customize.umr import UserModuleReloader @pytest.fixture def user_module(tmpdir): """Create a simple module in tmpdir as an example of a user module.""" if str(tmpdir) not in sys.path: sys.path.append(str(tmpdir)) def create_module(modname): modfile = tmpdir.mkdir(modname).join('bar.py') code = """ def square(x): return x**2 """ modfile.write(code) init_file = tmpdir.join(modname).join('__init__.py') init_file.write('#') return create_module def test_umr_run(user_module): """Test that UMR's run method is working correctly.""" # Create user module user_module('foo1') # Activate verbose mode in the UMR os.environ['SPY_UMR_VERBOSE'] = 'True' # Create UMR umr = UserModuleReloader() from foo1.bar import square assert umr.run() == ['foo1', 'foo1.bar'] def test_umr_previous_modules(user_module): """Test that UMR's previous_modules is working as expected.""" # Create user module user_module('foo2') # Create UMR umr = UserModuleReloader() import foo2 assert 'IPython' in umr.previous_modules assert 'foo2' not in umr.previous_modules def test_umr_namelist(): """Test that the UMR skips modules according to its name.""" umr = UserModuleReloader() assert umr.is_module_in_namelist('tensorflow') assert umr.is_module_in_namelist('pytorch') assert umr.is_module_in_namelist('spyder_kernels') assert not umr.is_module_in_namelist('foo') def test_umr_reload_modules(user_module): """Test that the UMR only tries to reload user modules.""" # Create user module user_module('foo3') # Create UMR umr = UserModuleReloader() # Don't reload stdlib modules import xml assert not umr.is_module_reloadable(xml, 'xml') # Don't reload third-party modules import numpy assert not umr.is_module_reloadable(numpy, 'numpy') # Reload user modules import foo3 assert umr.is_module_reloadable(foo3, 'foo3') spyder-kernels-3.0.3/spyder_kernels/customize/tests/test_utils.py000066400000000000000000000014641475072611100254630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- import os import sys from spyder_kernels.customize.utils import create_pathlist def test_user_sitepackages_in_pathlist(): """Test that we include user site-packages in pathlist.""" if sys.platform.startswith('linux'): user_path = 'local' elif (sys.platform == 'darwin' or sys.platform.startswith('freebsd')): user_path = os.path.expanduser('~/.local') else: user_path = 'Roaming' assert any([user_path in path for path in create_pathlist()]) spyder-kernels-3.0.3/spyder_kernels/customize/umr.py000066400000000000000000000067421475072611100227310ustar00rootroot00000000000000# Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) """User module reloader.""" import os import sys from spyder_kernels.customize.utils import path_is_library class UserModuleReloader: """ User Module Reloader (UMR) aims at deleting user modules to force Python to deeply reload them during import pathlist [list]: blacklist in terms of module path namelist [list]: blacklist in terms of module name """ def __init__(self, namelist=None, pathlist=None, shell=None): if namelist is None: namelist = [] else: try: namelist = namelist.split(',') except Exception: namelist = [] # Spyder modules spy_modules = ['spyder_kernels'] # Matplotlib modules mpl_modules = ['matplotlib', 'tkinter', 'Tkinter'] # Add other, necessary modules to the UMR blacklist # astropy: See spyder-ide/spyder#6962 # pytorch: See spyder-ide/spyder#7041 # fastmat: See spyder-ide/spyder#7190 # pythoncom: See spyder-ide/spyder#7190 # tensorflow: See spyder-ide/spyder#8697 other_modules = ['pytorch', 'pythoncom', 'tensorflow'] self.namelist = namelist + spy_modules + mpl_modules + other_modules self.pathlist = pathlist self._shell = shell # List of previously loaded modules self.previous_modules = list(sys.modules.keys()) # Check if the UMR is enabled or not enabled = os.environ.get("SPY_UMR_ENABLED", "") self.enabled = enabled.lower() == "true" # Check if the UMR should print the list of reloaded modules or not verbose = os.environ.get("SPY_UMR_VERBOSE", "") self.verbose = verbose.lower() == "true" def is_module_reloadable(self, module, modname): """Decide if a module is reloadable or not.""" if ( path_is_library(getattr(module, '__file__', None), self.pathlist) or self.is_module_in_namelist(modname) ): return False else: return True def is_module_in_namelist(self, modname): """Decide if a module can be reloaded or not according to its name.""" return set(modname.split('.')) & set(self.namelist) def run(self): """ Delete user modules to force Python to deeply reload them Do not del modules which are considered as system modules, i.e. modules installed in subdirectories of Python interpreter's binary Do not del C modules """ modnames_to_reload = [] for modname, module in list(sys.modules.items()): if modname not in self.previous_modules: # Decide if a module can be reloaded or not if self.is_module_reloadable(module, modname): modnames_to_reload.append(modname) del sys.modules[modname] else: continue # Report reloaded modules if self.verbose and modnames_to_reload: modnames = modnames_to_reload colors = {"dark": "33", "light": "31"} color = colors["dark"] if self._shell: color = colors[self._shell.get_spyder_theme()] content = ": "+", ".join(modnames) print(f"\x1b[4;{color}mReloaded modules\x1b[24m{content}\x1b[0m") return modnames_to_reload spyder-kernels-3.0.3/spyder_kernels/customize/utils.py000066400000000000000000000171121475072611100232570ustar00rootroot00000000000000# Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) """Utility functions.""" import ast import builtins import os import re import sys import sysconfig def create_pathlist(): """ Create list of Python library paths to be skipped from module reloading and Pdb steps. """ # Get standard installation paths try: paths = sysconfig.get_paths() standard_paths = [paths['stdlib'], paths['purelib'], paths['scripts'], paths['data']] except Exception: standard_paths = [] # Get user installation path # See spyder-ide/spyder#8776 try: import site if getattr(site, 'getusersitepackages', False): # Virtualenvs don't have this function but # conda envs do user_path = [site.getusersitepackages()] elif getattr(site, 'USER_SITE', False): # However, it seems virtualenvs have this # constant user_path = [site.USER_SITE] else: user_path = [] except Exception: user_path = [] return standard_paths + user_path def path_is_library(path, initial_pathlist=None): """Decide if a path is in user code or a library according to its path.""" # Compute DEFAULT_PATHLIST only once and make it global to reuse it # in any future call of this function. if 'DEFAULT_PATHLIST' not in globals(): global DEFAULT_PATHLIST DEFAULT_PATHLIST = create_pathlist() if initial_pathlist is None: initial_pathlist = [] pathlist = initial_pathlist + DEFAULT_PATHLIST if path is None: # Path probably comes from a C module that is statically linked # into the interpreter. There is no way to know its path, so we # choose to ignore it. return True elif any([p in path for p in pathlist]): # We don't want to consider paths that belong to the standard # library or installed to site-packages. return True elif os.name == 'nt': if re.search(r'.*\\pkgs\\.*', path): return True else: return False elif not os.name == 'nt': # Paths containing the strings below can be part of the default # Linux installation, Homebrew or the user site-packages in a # virtualenv. patterns = [ r'^/usr/lib.*', r'^/usr/local/lib.*', r'^/usr/.*/dist-packages/.*', r'^/home/.*/.local/lib.*', r'^/Library/.*', r'^/Users/.*/Library/.*', r'^/Users/.*/.local/.*', ] if [p for p in patterns if re.search(p, path)]: return True else: return False else: return False def capture_last_Expr(code_ast, out_varname, global_ns): """Parse line and modify code to capture in globals the last expression.""" # Modify ast code to capture the last expression capture_last_expression = False if ( len(code_ast.body) and isinstance(code_ast.body[-1], ast.Expr) ): global_ns["__spyder_builtins__"] = builtins capture_last_expression = True expr_node = code_ast.body[-1] # Create new assign node assign_node = ast.parse( '__spyder_builtins__.globals()[{}] = None'.format( repr(out_varname))).body[0] # Replace None by the value assign_node.value = expr_node.value # Fix line number and column offset assign_node.lineno = expr_node.lineno assign_node.col_offset = expr_node.col_offset if sys.version_info[:2] >= (3, 8): # Exists from 3.8, necessary from 3.11 assign_node.end_lineno = expr_node.end_lineno if assign_node.lineno == assign_node.end_lineno: # Add '__spyder_builtins__.globals()[{}] = ' and remove 'None' assign_node.end_col_offset += expr_node.end_col_offset - 4 else: assign_node.end_col_offset = expr_node.end_col_offset code_ast.body[-1] = assign_node return code_ast, capture_last_expression def exec_encapsulate_locals( code_ast, globals, locals, exec_fun=None, filename=None ): """ Execute by encapsulating locals if needed. Notes ----- * In general, the dict returned by locals() might or might not be modified. In this case, the encapsulated dict can not. """ use_locals_hack = locals is not None and locals is not globals if use_locals_hack: globals["__spyder_builtins__"] = builtins # Mitigates a behaviour of CPython that makes it difficult # to work with exec and the local namespace # See: # - https://bugs.python.org/issue41918 # - https://bugs.python.org/issue46153 # - https://bugs.python.org/issue21161 # - spyder-ide/spyder#13909 # - spyder-ide/spyder-kernels#345 # # The idea here is that the best way to emulate being in a # function is to actually execute the code in a function. # A function called `_spyderpdb_code` is created and # called. It will first load the locals, execute the code, # and then update the locals. # # One limitation of this approach is that locals() is only # a copy of the curframe locals. This means that closures # for example are early binding instead of late binding. # Create a function indent = " " code = ["def _spyderpdb_code():"] # Add locals in globals # If the debugger is recursive, the globals could already # have a _spyderpdb_locals as it might be shared between # levels if "_spyderpdb_locals" in globals: globals["_spyderpdb_locals"].append(locals) else: globals["_spyderpdb_locals"] = [locals] # Load locals if they have a valid name # In comprehensions, locals could contain ".0" for example code += [indent + "{k} = _spyderpdb_locals[-1]['{k}']".format( k=k) for k in locals if k.isidentifier()] # The code comes here # Update the locals code += [indent + "_spyderpdb_locals[-1].update(" "__spyder_builtins__.locals())"] # Run the function code += ["_spyderpdb_code()"] # Parse the function fun_ast = ast.parse('\n'.join(code) + '\n') # Inject code_ast in the function before the locals update fun_ast.body[0].body = ( fun_ast.body[0].body[:-1] # The locals + code_ast.body # Code to run + fun_ast.body[0].body[-1:] # Locals update ) code_ast = fun_ast try: if exec_fun is None: exec_fun = exec if filename is None: filename = "" exec_fun(compile(code_ast, filename, "exec"), globals, None) finally: if use_locals_hack: # Cleanup code globals.pop("_spyderpdb_code", None) if len(globals["_spyderpdb_locals"]) > 1: del globals["_spyderpdb_locals"][-1] else: del globals["_spyderpdb_locals"] def canonic(filename): """ Return canonical form of filename. This is a copy of bdb.canonic, so that the debugger will process filenames in the same way """ if filename == "<" + filename[1:-1] + ">": return filename canonic = os.path.abspath(filename) canonic = os.path.normcase(canonic) return canonic spyder-kernels-3.0.3/spyder_kernels/utils/000077500000000000000000000000001475072611100206615ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/utils/__init__.py000066400000000000000000000005341475072611100227740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Utilities """ spyder-kernels-3.0.3/spyder_kernels/utils/dochelpers.py000066400000000000000000000265111475072611100233700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Utilities and wrappers around inspect module""" import builtins import inspect import re SYMBOLS = r"[^\'\"a-zA-Z0-9_.]" def getobj(txt, last=False): """Return the last valid object name in string""" txt_end = "" for startchar, endchar in ["[]", "()"]: if txt.endswith(endchar): pos = txt.rfind(startchar) if pos: txt_end = txt[pos:] txt = txt[:pos] tokens = re.split(SYMBOLS, txt) token = None try: while token is None or re.match(SYMBOLS, token): token = tokens.pop() if token.endswith('.'): token = token[:-1] if token.startswith('.'): # Invalid object name return None if last: #XXX: remove this statement as well as the "last" argument token += txt[ txt.rfind(token) + len(token) ] token += txt_end if token: return token except IndexError: return None def getobjdir(obj): """ For standard objects, will simply return dir(obj) In special cases (e.g. WrapITK package), will return only string elements of result returned by dir(obj) """ return [item for item in dir(obj) if isinstance(item, str)] def getdoc(obj): """ Return text documentation from an object. This comes in a form of dictionary with four keys: name: The name of the inspected object argspec: It's argspec note: A phrase describing the type of object (function or method) we are inspecting, and the module it belongs to. docstring: It's docstring """ docstring = inspect.getdoc(obj) or inspect.getcomments(obj) or '' # Most of the time doc will only contain ascii characters, but there are # some docstrings that contain non-ascii characters. Not all source files # declare their encoding in the first line, so querying for that might not # yield anything, either. So assume the most commonly used # multi-byte file encoding (which also covers ascii). try: docstring = str(docstring) except: pass # Doc dict keys doc = {'name': '', 'argspec': '', 'note': '', 'docstring': docstring} if callable(obj): try: name = obj.__name__ except AttributeError: doc['docstring'] = docstring return doc if inspect.ismethod(obj): imclass = obj.__self__.__class__ if obj.__self__ is not None: doc['note'] = 'Method of %s instance' \ % obj.__self__.__class__.__name__ else: doc['note'] = 'Unbound %s method' % imclass.__name__ obj = obj.__func__ elif hasattr(obj, '__module__'): doc['note'] = 'Function of %s module' % obj.__module__ else: doc['note'] = 'Function' doc['name'] = obj.__name__ if inspect.isfunction(obj): # This is necessary to catch errors for objects without a # signature, like numpy.where. # Fixes spyder-ide/spyder#21148 try: sig = inspect.signature(obj) except ValueError: sig = getargspecfromtext(doc['docstring']) if not sig: sig = '(...)' doc['argspec'] = str(sig) if name == '': doc['name'] = name + ' lambda ' doc['argspec'] = doc['argspec'][1:-1] # remove parentheses else: argspec = getargspecfromtext(doc['docstring']) if argspec: doc['argspec'] = argspec # Many scipy and numpy docstrings begin with a function # signature on the first line. This ends up begin redundant # when we are using title and argspec to create the # rich text "Definition:" field. We'll carefully remove this # redundancy but only under a strict set of conditions: # Remove the starting charaters of the 'doc' portion *iff* # the non-whitespace characters on the first line # match *exactly* the combined function title # and argspec we determined above. signature = doc['name'] + doc['argspec'] docstring_blocks = doc['docstring'].split("\n\n") first_block = docstring_blocks[0].strip() if first_block == signature: doc['docstring'] = doc['docstring'].replace( signature, '', 1).lstrip() else: doc['argspec'] = '(...)' # Remove self from argspec argspec = doc['argspec'] doc['argspec'] = argspec.replace('(self)', '()').replace('(self, ', '(') return doc def getsource(obj): """Wrapper around inspect.getsource""" try: try: src = str(inspect.getsource(obj)) except TypeError: if hasattr(obj, '__class__'): src = str(inspect.getsource(obj.__class__)) else: # Bindings like VTK or ITK require this case src = getdoc(obj) return src except (TypeError, IOError): return def getsignaturefromtext(text, objname): """Get object signature from text (i.e. object documentation).""" if isinstance(text, dict): text = text.get('docstring', '') # Regexps args_re = r'(\(.+?\))' if objname: signature_re = objname + args_re else: identifier_re = r'(\w+)' signature_re = identifier_re + args_re # Grabbing signatures if not text: text = '' sigs = re.findall(signature_re, text) # The most relevant signature is usually the first one. There could be # others in doctests or other places, but those are not so important. sig = '' if sigs: # Default signatures returned by IPython. # Notes: # * These are not real signatures but only used to provide a # placeholder. # * We skip them if we can find other signatures in `text`. # * This is necessary because we also use this function in Spyder # to parse the content of inspect replies that come from the # kernel, which can include these signatures. default_ipy_sigs = ['(*args, **kwargs)', '(self, /, *args, **kwargs)'] if objname: real_sigs = [s for s in sigs if s not in default_ipy_sigs] if real_sigs: sig = real_sigs[0] else: sig = sigs[0] else: valid_sigs = [s for s in sigs if s[0].isidentifier()] if valid_sigs: real_sigs = [ s for s in valid_sigs if s[1] not in default_ipy_sigs ] if real_sigs: sig = real_sigs[0][1] else: sig = valid_sigs[0][1] return sig def getargspecfromtext(text): """ Try to get the formatted argspec of a callable from the first block of its docstring. This will return something like `(x, y, k=1)`. """ blocks = text.split("\n\n") first_block = blocks[0].strip().replace('\n', '') return getsignaturefromtext(first_block, '') def getargsfromtext(text, objname): """Get arguments from text (object documentation).""" signature = getsignaturefromtext(text, objname) if signature: argtxt = signature[signature.find('(') + 1:-1] return argtxt.split(',') def getargsfromdoc(obj): """Get arguments from object doc""" if obj.__doc__ is not None: return getargsfromtext(obj.__doc__, obj.__name__) def getargs(obj): """Get the names and default values of a function's arguments""" if inspect.isfunction(obj) or inspect.isbuiltin(obj): func_obj = obj elif inspect.ismethod(obj): func_obj = obj.__func__ elif inspect.isclass(obj) and hasattr(obj, '__init__'): func_obj = getattr(obj, '__init__') else: return [] if not hasattr(func_obj, '__code__'): # Builtin: try to extract info from doc args = getargsfromdoc(func_obj) if args is not None: return args else: # Example: PyQt5 return getargsfromdoc(obj) args, _, _ = inspect.getargs(func_obj.__code__) if not args: return getargsfromdoc(obj) # Supporting tuple arguments in def statement: for i_arg, arg in enumerate(args): if isinstance(arg, list): args[i_arg] = "(%s)" % ", ".join(arg) defaults = func_obj.__defaults__ if defaults is not None: for index, default in enumerate(defaults): args[index + len(args) - len(defaults)] += '=' + repr(default) if inspect.isclass(obj) or inspect.ismethod(obj): if len(args) == 1: return None # Remove 'self' from args if 'self' in args: args.remove('self') return args def getargtxt(obj, one_arg_per_line=True): """ Get the names and default values of a function's arguments Return list with separators (', ') formatted for calltips """ args = getargs(obj) if args: sep = ', ' textlist = None for i_arg, arg in enumerate(args): if textlist is None: textlist = [''] textlist[-1] += arg if i_arg < len(args)-1: textlist[-1] += sep if len(textlist[-1]) >= 32 or one_arg_per_line: textlist.append('') if inspect.isclass(obj) or inspect.ismethod(obj): if len(textlist) == 1: return None if 'self'+sep in textlist: textlist.remove('self'+sep) return textlist def isdefined(obj, force_import=False, namespace=None): """Return True if object is defined in namespace If namespace is None --> namespace = locals()""" if namespace is None: namespace = locals() attr_list = obj.split('.') base = attr_list.pop(0) if len(base) == 0: return False if base not in builtins.__dict__ and base not in namespace: if force_import: try: module = __import__(base, globals(), namespace) if base not in globals(): globals()[base] = module namespace[base] = module except Exception: return False else: return False for attr in attr_list: try: attr_not_found = not hasattr(eval(base, namespace), attr) except (AttributeError, SyntaxError, TypeError): return False if attr_not_found: if force_import: try: __import__(base+'.'+attr, globals(), namespace) except (ImportError, SyntaxError): return False else: return False base += '.'+attr return True spyder-kernels-3.0.3/spyder_kernels/utils/iofuncs.py000066400000000000000000000521311475072611100227030ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Input/Output Utilities Note: 'load' functions has to return a dictionary from which a globals() namespace may be updated """ # Standard library imports import sys import os import os.path as osp import tarfile import tempfile import shutil import types import json import inspect import dis import copy import glob import pickle # Local imports from spyder_kernels.utils.lazymodules import ( FakeObject, numpy as np, pandas as pd, PIL, scipy as sp) # ---- For Matlab files # ----------------------------------------------------------------------------- class MatlabStruct(dict): """ Matlab style struct, enhanced. Supports dictionary and attribute style access. Can be pickled, and supports code completion in a REPL. Examples ======== >>> from spyder_kernels.utils.iofuncs import MatlabStruct >>> a = MatlabStruct() >>> a.b = 'spam' # a["b"] == 'spam' >>> a.c["d"] = 'eggs' # a.c.d == 'eggs' >>> print(a) {'c': {'d': 'eggs'}, 'b': 'spam'} """ def __getattr__(self, attr): """Access the dictionary keys for unknown attributes.""" try: return self[attr] except KeyError: msg = "'MatlabStruct' object has no attribute %s" % attr raise AttributeError(msg) def __getitem__(self, attr): """ Get a dict value; create a MatlabStruct if requesting a submember. Do not create a key if the attribute starts with an underscore. """ if attr in self.keys() or attr.startswith('_'): return dict.__getitem__(self, attr) frame = inspect.currentframe() # step into the function that called us if frame.f_back.f_back and self._is_allowed(frame.f_back.f_back): dict.__setitem__(self, attr, MatlabStruct()) elif self._is_allowed(frame.f_back): dict.__setitem__(self, attr, MatlabStruct()) return dict.__getitem__(self, attr) def _is_allowed(self, frame): """Check for allowed op code in the calling frame""" allowed = [dis.opmap['STORE_ATTR'], dis.opmap['LOAD_CONST'], dis.opmap.get('STOP_CODE', 0)] bytecode = frame.f_code.co_code instruction = bytecode[frame.f_lasti + 3] return instruction in allowed __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ @property def __dict__(self): """Allow for code completion in a REPL""" return self.copy() def get_matlab_value(val): """ Extract a value from a Matlab file From the oct2py project, see https://pythonhosted.org/oct2py/conversions.html """ # Extract each item of a list. if isinstance(val, list): return [get_matlab_value(v) for v in val] # Ignore leaf objects. if not isinstance(val, np.ndarray): return val # Convert user defined classes. if hasattr(val, 'classname'): out = dict() for name in val.dtype.names: out[name] = get_matlab_value(val[name].squeeze().tolist()) cls = type(val.classname, (object,), out) return cls() # Extract struct data. elif val.dtype.names: out = MatlabStruct() for name in val.dtype.names: out[name] = get_matlab_value(val[name].squeeze().tolist()) val = out # Extract cells. elif val.dtype.kind == 'O': val = val.squeeze().tolist() if not isinstance(val, list): val = [val] val = get_matlab_value(val) # Compress singleton values. elif val.size == 1: val = val.item() # Compress empty values. elif val.size == 0: if val.dtype.kind in 'US': val = '' else: val = [] return val def load_matlab(filename): if sp.io is FakeObject: return None, '' try: out = sp.io.loadmat(filename, struct_as_record=True) data = dict() for (key, value) in out.items(): data[key] = get_matlab_value(value) return data, None except Exception as error: return None, str(error) def save_matlab(data, filename): if sp.io is FakeObject: return try: sp.io.savemat(filename, data, oned_as='row') except Exception as error: return str(error) # ---- For arrays # ----------------------------------------------------------------------------- def load_array(filename): if np.load is FakeObject: return None, '' try: name = osp.splitext(osp.basename(filename))[0] data = np.load(filename) if isinstance(data, np.lib.npyio.NpzFile): return dict(data), None elif hasattr(data, 'keys'): return data, None else: return {name: data}, None except Exception as error: return None, str(error) def __save_array(data, basename, index): """Save numpy array""" fname = basename + '_%04d.npy' % index np.save(fname, data) return fname # ---- For PIL images # ----------------------------------------------------------------------------- if sys.byteorder == 'little': _ENDIAN = '<' else: _ENDIAN = '>' DTYPES = { "1": ('|b1', None), "L": ('|u1', None), "I": ('%si4' % _ENDIAN, None), "F": ('%sf4' % _ENDIAN, None), "I;16": ('|u2', None), "I;16S": ('%si2' % _ENDIAN, None), "P": ('|u1', None), "RGB": ('|u1', 3), "RGBX": ('|u1', 4), "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), "YCbCr": ('|u1', 4), } def __image_to_array(filename): img = PIL.Image.open(filename) try: dtype, extra = DTYPES[img.mode] except KeyError: raise RuntimeError("%s mode is not supported" % img.mode) shape = (img.size[1], img.size[0]) if extra is not None: shape += (extra,) return np.array(img.getdata(), dtype=np.dtype(dtype)).reshape(shape) def load_image(filename): if PIL.Image is FakeObject or np.array is FakeObject: return None, '' try: name = osp.splitext(osp.basename(filename))[0] return {name: __image_to_array(filename)}, None except Exception as error: return None, str(error) # ---- For misc formats # ----------------------------------------------------------------------------- def load_pickle(filename): """Load a pickle file as a dictionary""" try: if pd.read_pickle is not FakeObject: return pd.read_pickle(filename), None else: with open(filename, 'rb') as fid: data = pickle.load(fid) return data, None except Exception as err: return None, str(err) def load_json(filename): """Load a json file as a dictionary""" try: with open(filename, 'r') as fid: data = json.load(fid) return data, None except Exception as err: return None, str(err) # ---- For Spydata files # ----------------------------------------------------------------------------- def save_dictionary(data, filename): """Save dictionary in a single file .spydata file""" filename = osp.abspath(filename) old_cwd = os.getcwd() os.chdir(osp.dirname(filename)) error_message = None skipped_keys = [] data_copy = {} try: # Copy dictionary before modifying it to fix #6689 for obj_name, obj_value in data.items(): # Skip modules, since they can't be pickled, users virtually never # would want them to be and so they don't show up in the skip list. # Skip callables, since they are only pickled by reference and thus # must already be present in the user's environment anyway. if not (callable(obj_value) or isinstance(obj_value, types.ModuleType)): # If an object cannot be deepcopied, then it cannot be pickled. # Ergo, we skip it and list it later. try: data_copy[obj_name] = copy.deepcopy(obj_value) except Exception: skipped_keys.append(obj_name) data = data_copy if not data: raise RuntimeError('No supported objects to save') saved_arrays = {} if np.ndarray is not FakeObject: # Saving numpy arrays with np.save arr_fname = osp.splitext(filename)[0] for name in list(data.keys()): try: if (isinstance(data[name], np.ndarray) and data[name].size > 0): # Save arrays at data root fname = __save_array(data[name], arr_fname, len(saved_arrays)) saved_arrays[(name, None)] = osp.basename(fname) data.pop(name) elif isinstance(data[name], (list, dict)): # Save arrays nested in lists or dictionaries if isinstance(data[name], list): iterator = enumerate(data[name]) else: iterator = iter(list(data[name].items())) to_remove = [] for index, value in iterator: if (isinstance(value, np.ndarray) and value.size > 0): fname = __save_array(value, arr_fname, len(saved_arrays)) saved_arrays[(name, index)] = ( osp.basename(fname)) to_remove.append(index) for index in sorted(to_remove, reverse=True): data[name].pop(index) except (RuntimeError, pickle.PicklingError, TypeError, AttributeError, IndexError): # If an array can't be saved with numpy for some reason, # leave the object intact and try to save it normally. pass if saved_arrays: data['__saved_arrays__'] = saved_arrays pickle_filename = osp.splitext(filename)[0] + '.pickle' # Attempt to pickle everything. # If pickling fails, iterate through to eliminate problem objs & retry. with open(pickle_filename, 'w+b') as fdesc: try: pickle.dump(data, fdesc, protocol=2) except (pickle.PicklingError, AttributeError, TypeError, ImportError, IndexError, RuntimeError): data_filtered = {} for obj_name, obj_value in data.items(): try: pickle.dumps(obj_value, protocol=2) except Exception: skipped_keys.append(obj_name) else: data_filtered[obj_name] = obj_value if not data_filtered: raise RuntimeError('No supported objects to save') pickle.dump(data_filtered, fdesc, protocol=2) # Use PAX (POSIX.1-2001) format instead of default GNU. # This improves interoperability and UTF-8/long variable name support. with tarfile.open(filename, "w", format=tarfile.PAX_FORMAT) as tar: for fname in ([pickle_filename] + [fn for fn in list(saved_arrays.values())]): tar.add(osp.basename(fname)) os.remove(fname) except (RuntimeError, pickle.PicklingError, TypeError) as error: error_message = str(error) else: if skipped_keys: skipped_keys.sort() error_message = ('Some objects could not be saved: ' + ', '.join(skipped_keys)) finally: os.chdir(old_cwd) return error_message def is_within_directory(directory, target): """Check if a file is within a directory.""" abs_directory = os.path.abspath(directory) abs_target = os.path.abspath(target) prefix = os.path.commonprefix([abs_directory, abs_target]) return prefix == abs_directory def safe_extract(tar, path=".", members=None, *, numeric_owner=False): """Safely extract a tar file.""" for member in tar.getmembers(): member_path = os.path.join(path, member.name) if not is_within_directory(path, member_path): raise Exception( f"Attempted path traversal in tar file {tar.name!r}" ) tar.extractall(path, members, numeric_owner=numeric_owner) def load_dictionary(filename): """Load dictionary from .spydata file""" filename = osp.abspath(filename) old_cwd = os.getcwd() tmp_folder = tempfile.mkdtemp() os.chdir(tmp_folder) data = None error_message = None try: with tarfile.open(filename, "r") as tar: safe_extract(tar) pickle_filename = glob.glob('*.pickle')[0] # 'New' format (Spyder >=2.2) with open(pickle_filename, 'rb') as fdesc: data = pickle.loads(fdesc.read()) saved_arrays = {} if np.load is not FakeObject: # Loading numpy arrays saved with np.save try: saved_arrays = data.pop('__saved_arrays__') for (name, index), fname in list(saved_arrays.items()): arr = np.load(osp.join(tmp_folder, fname), allow_pickle=True) if index is None: data[name] = arr elif isinstance(data[name], dict): data[name][index] = arr else: data[name].insert(index, arr) except KeyError: pass # Except AttributeError from e.g. trying to load function no longer present except (AttributeError, EOFError, ValueError) as error: error_message = str(error) # To ensure working dir gets changed back and temp dir wiped no matter what finally: os.chdir(old_cwd) try: shutil.rmtree(tmp_folder) except OSError as error: error_message = str(error) return data, error_message # ---- For HDF5 files # ----------------------------------------------------------------------------- def load_hdf5(filename): """ Load an hdf5 file. Notes ----- - This is a fairly dumb implementation which reads the whole HDF5 file into Spyder's variable explorer. Since HDF5 files are designed for storing very large data-sets, it may be much better to work directly with the HDF5 objects, thus keeping the data on disk. Nonetheless, this gives quick and dirty but convenient access to them. - There is no support for creating files with compression, chunking etc, although these can be read without problem. - When reading an HDF5 file with sub-groups, groups in the file will correspond to dictionaries with the same layout. """ def get_group(group): contents = {} for name, obj in list(group.items()): if isinstance(obj, h5py.Dataset): contents[name] = np.array(obj) elif isinstance(obj, h5py.Group): # it is a group, so call self recursively contents[name] = get_group(obj) # other objects such as links are ignored return contents try: import h5py f = h5py.File(filename, 'r') contents = get_group(f) f.close() return contents, None except Exception as error: return None, str(error) def save_hdf5(data, filename): """ Save an hdf5 file. Notes ----- - All datatypes to be saved must be convertible to a numpy array, otherwise an exception will be raised. - Data attributes are currently ignored. - When saving data after reading it with load_hdf5, dictionaries are not turned into HDF5 groups. """ try: import h5py f = h5py.File(filename, 'w') for key, value in list(data.items()): f[key] = np.array(value) f.close() except Exception as error: return str(error) # ---- For DICOM files # ----------------------------------------------------------------------------- def load_dicom(filename): """Load a DICOM files.""" try: from pydicom import dicomio name = osp.splitext(osp.basename(filename))[0] try: # For Pydicom 3/Python 3.10+ data = dicomio.dcmread(filename, force=True) except TypeError: data = dicomio.dcmread(filename) except AttributeError: # For Pydicom 2/Python 3.9- try: data = dicomio.read_file(filename, force=True) except TypeError: data = dicomio.read_file(filename) arr = data.pixel_array return {name: arr}, None except Exception as error: return None, str(error) # ---- Class to group all IO functionality # ----------------------------------------------------------------------------- class IOFunctions: def __init__(self): self.load_extensions = None self.save_extensions = None self.load_filters = None self.save_filters = None self.load_funcs = None self.save_funcs = None def setup(self): iofuncs = self.get_internal_funcs() load_extensions = {} save_extensions = {} load_funcs = {} save_funcs = {} load_filters = [] save_filters = [] load_ext = [] for ext, name, loadfunc, savefunc in iofuncs: filter_str = str(name + " (*%s)" % ext) if loadfunc is not None: load_filters.append(filter_str) load_extensions[filter_str] = ext load_funcs[ext] = loadfunc load_ext.append(ext) if savefunc is not None: save_extensions[filter_str] = ext save_filters.append(filter_str) save_funcs[ext] = savefunc load_filters.insert( 0, str("Supported files" + " (*" + " *".join(load_ext) + ")") ) load_filters.append(str("All files (*.*)")) self.load_filters = "\n".join(load_filters) self.save_filters = "\n".join(save_filters) self.load_funcs = load_funcs self.save_funcs = save_funcs self.load_extensions = load_extensions self.save_extensions = save_extensions def get_internal_funcs(self): return [ ('.spydata', "Spyder data files", load_dictionary, save_dictionary), ('.npy', "NumPy arrays", load_array, None), ('.npz', "NumPy zip arrays", load_array, None), ('.mat', "Matlab files", load_matlab, save_matlab), ('.csv', "CSV text files", 'import_wizard', None), ('.txt', "Text files", 'import_wizard', None), ('.jpg', "JPEG images", load_image, None), ('.png', "PNG images", load_image, None), ('.gif', "GIF images", load_image, None), ('.tif', "TIFF images", load_image, None), ('.pkl', "Pickle files", load_pickle, None), ('.pickle', "Pickle files", load_pickle, None), ('.json', "JSON files", load_json, None), ('.h5', "HDF5 files", load_hdf5, save_hdf5), ('.dcm', "DICOM images", load_dicom, None), ] def save(self, data, filename): ext = osp.splitext(filename)[1].lower() if ext in self.save_funcs: return self.save_funcs[ext](data, filename) else: return "Unsupported file type '%s'" % ext def load(self, filename): ext = osp.splitext(filename)[1].lower() if ext in self.load_funcs: return self.load_funcs[ext](filename) else: return None, "Unsupported file type '%s'" % ext iofunctions = IOFunctions() iofunctions.setup() # ---- Test # ----------------------------------------------------------------------------- if __name__ == "__main__": import datetime testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]} testdate = datetime.date(1945, 5, 8) example = {'str': 'kjkj kj k j j kj k jkj', 'unicode': u'éù', 'list': [1, 3, [4, 5, 6], 'kjkj', None], 'tuple': ([1, testdate, testdict], 'kjkj', None), 'dict': testdict, 'float': 1.2233, 'array': np.random.rand(4000, 400), 'empty_array': np.array([]), 'date': testdate, 'datetime': datetime.datetime(1945, 5, 8), } import time t0 = time.time() save_dictionary(example, "test.spydata") print(" Data saved in %.3f seconds" % (time.time()-t0)) t0 = time.time() example2, ok = load_dictionary("test.spydata") os.remove("test.spydata") print("Data loaded in %.3f seconds" % (time.time()-t0)) spyder-kernels-3.0.3/spyder_kernels/utils/lazymodules.py000066400000000000000000000040561475072611100236100ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Lazy modules. They are useful to not import big modules until it's really necessary. """ from spyder_kernels.utils.misc import is_module_installed # ============================================================================= # Auxiliary classes # ============================================================================= class FakeObject: """Fake class used in replacement of missing objects""" pass class LazyModule: """Lazy module loader class.""" def __init__(self, modname, second_level_attrs=None): """ Lazy module loader class. Parameters ---------- modname: str Module name to lazy load. second_level_attrs: list (optional) List of second level attributes to add to the FakeObject that stands for the module in case it's not found. """ self.__spy_modname__ = modname self.__spy_mod__ = FakeObject # Set required second level attributes if second_level_attrs is not None: for attr in second_level_attrs: setattr(self.__spy_mod__, attr, FakeObject) def __getattr__(self, name): if is_module_installed(self.__spy_modname__): self.__spy_mod__ = __import__(self.__spy_modname__) else: return self.__spy_mod__ return getattr(self.__spy_mod__, name) # ============================================================================= # Lazy modules # ============================================================================= numpy = LazyModule('numpy', ['MaskedArray']) pandas = LazyModule('pandas') PIL = LazyModule('PIL.Image', ['Image']) bs4 = LazyModule('bs4', ['NavigableString']) scipy = LazyModule('scipy.io') spyder-kernels-3.0.3/spyder_kernels/utils/misc.py000066400000000000000000000030341475072611100221660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Miscellaneous utilities""" import re from functools import lru_cache @lru_cache(maxsize=100) def is_module_installed(module_name): """ Simpler version of spyder.utils.programs.is_module_installed. """ try: mod = __import__(module_name) # This is necessary to not report that the module is installed # when only its __pycache__ directory is present. if getattr(mod, '__file__', None): return True else: return False except Exception: # Module is not installed return False def fix_reference_name(name, blacklist=None): """Return a syntax-valid Python reference name from an arbitrary name""" name = "".join(re.split(r'[^0-9a-zA-Z_]', name)) while name and not re.match(r'([a-zA-Z]+[0-9a-zA-Z_]*)$', name): if not re.match(r'[a-zA-Z]', name[0]): name = name[1:] continue name = str(name) if not name: name = "data" if blacklist is not None and name in blacklist: get_new_name = lambda index: name+('_%03d' % index) index = 0 while get_new_name(index) in blacklist: index += 1 name = get_new_name(index) return name spyder-kernels-3.0.3/spyder_kernels/utils/mpl.py000066400000000000000000000022311475072611100220210ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Matplotlib utilities.""" from spyder_kernels.utils.misc import is_module_installed # Inline backend if is_module_installed('matplotlib_inline'): inline_backend = 'module://matplotlib_inline.backend_inline' else: inline_backend = 'module://ipykernel.pylab.backend_inline' # Mapping of matlotlib backends options to Spyder MPL_BACKENDS_TO_SPYDER = { 'inline': 'inline', # For Matplotlib >=3.9 inline_backend: "inline", # For Matplotlib <3.9 'qt5agg': 'qt', 'qtagg': 'qt', # For Matplotlib 3.5+ 'tkagg': 'tk', 'macosx': 'osx', } def automatic_backend(): """Get Matplolib automatic backend option.""" if is_module_installed('PyQt5'): auto_backend = 'qt' elif is_module_installed('_tkinter'): auto_backend = 'tk' else: auto_backend = 'inline' return auto_backend spyder-kernels-3.0.3/spyder_kernels/utils/nsview.py000066400000000000000000000575671475072611100225720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Utilities to build a namespace view. """ from itertools import islice import inspect import re from spyder_kernels.utils.lazymodules import ( bs4, FakeObject, numpy as np, pandas as pd, PIL) #============================================================================== # Numpy support #============================================================================== def get_numeric_numpy_types(): return (np.int64, np.int32, np.int16, np.int8, np.uint64, np.uint32, np.uint16, np.uint8, np.float64, np.float32, np.float16, np.complex64, np.complex128, np.bool_) def get_numpy_dtype(obj): """ Return Numpy data type associated to `obj`. Return None if Numpy is not available, if we get errors or if `obj` is not a Numpy array or scalar. """ # Check if NumPy is available if np.ndarray is not FakeObject: # All Numpy scalars inherit from np.generic and all Numpy arrays # inherit from np.ndarray. If we check that we are certain we have one # of these types then we are less likely to generate an exception # below. # Note: The try/except is necessary to fix spyder-ide/spyder#19516. try: scalar_or_array = ( isinstance(obj, np.generic) or isinstance(obj, np.ndarray) ) except Exception: return if scalar_or_array: try: return obj.dtype.type except (AttributeError, RuntimeError): # AttributeError: some NumPy objects have no dtype attribute # RuntimeError: happens with NetCDF objects (Issue 998) return def get_numpy_type_string(value): """Get the type of a Numpy object as a string.""" np_dtype = get_numpy_dtype(value) if np_dtype is None or not hasattr(value, 'size'): return 'Unknown' elif value.size == 1: return 'Scalar' else: return 'Array' #============================================================================== # Misc. #============================================================================== def address(obj): """Return object address as a string: ''""" return "<%s @ %s>" % (obj.__class__.__name__, hex(id(obj)).upper().replace('X', 'x')) def try_to_eval(value): """Try to eval value""" try: return eval(value) except (NameError, SyntaxError, ImportError): return value def get_size(item): """Return shape/size/len of an item of arbitrary type""" try: if ( hasattr(item, 'size') and hasattr(item.size, 'compute') or hasattr(item, 'shape') and hasattr(item.shape, 'compute') ): # This is necessary to avoid an error when trying to # get the size/shape of dask objects. We don't compute the # size/shape since such operation could be expensive. # Fixes spyder-ide/spyder#16844 return 1 elif ( hasattr(item, 'shape') and isinstance(item.shape, (tuple, np.integer)) ): try: if item.shape: # This is needed since values could return as # `shape` an instance of a `tuple` subclass. # See spyder-ide/spyder#16348 if isinstance(item.shape, tuple): return tuple(item.shape) return item.shape else: # Scalar value return 1 except RecursionError: # This is necessary to avoid an error when trying to # get the shape of these objects. # Fixes spyder-ide/spyder-kernels#217 return (-1, -1) elif (hasattr(item, 'size') and isinstance(item.size, (tuple, np.integer))): try: return item.size except RecursionError: return (-1, -1) elif hasattr(item, '__len__'): return len(item) else: return 1 except Exception: # There is one item return 1 def get_object_attrs(obj): """ Get the attributes of an object using dir. This filters protected attributes """ attrs = [k for k in dir(obj) if not k.startswith('__')] if not attrs: attrs = dir(obj) return attrs #============================================================================== # Date and datetime objects support #============================================================================== import datetime try: from dateutil.parser import parse as dateparse except: def dateparse(datestr): # analysis:ignore """Just for 'year, month, day' strings""" return datetime.datetime( *list(map(int, datestr.split(','))) ) def datestr_to_datetime(value): rp = value.rfind('(')+1 v = dateparse(value[rp:-1]) print(value, "-->", v) # spyder: test-skip return v def str_to_timedelta(value): """Convert a string to a datetime.timedelta value. The following strings are accepted: - 'datetime.timedelta(1, 5, 12345)' - 'timedelta(1, 5, 12345)' - '(1, 5, 12345)' - '1, 5, 12345' - '1' if there are less then three parameters, the missing parameters are assumed to be 0. Variations in the spacing of the parameters are allowed. Raises: ValueError for strings not matching the above criterion. """ m = re.match(r'^(?:(?:datetime\.)?timedelta)?' r'\(?' r'([^)]*)' r'\)?$', value) if not m: raise ValueError('Invalid string for datetime.timedelta') args = [int(a.strip()) for a in m.group(1).split(',')] return datetime.timedelta(*args) #============================================================================== # Supported types #============================================================================== def is_editable_type(value): """ Return True if data type is editable with a standard GUI-based editor, like CollectionsEditor, ArrayEditor, QDateEdit or a simple QLineEdit. """ if not is_known_type(value): return False else: supported_types = [ 'bool', 'int', 'long', 'float', 'complex', 'list', 'set', 'dict', 'tuple', 'str', 'unicode', 'NDArray', 'MaskedArray', 'Matrix', 'DataFrame', 'Series', 'PIL.Image.Image', 'datetime.date', 'datetime.timedelta' ] if (get_type_string(value) not in supported_types and not isinstance(value, pd.Index)): np_dtype = get_numpy_dtype(value) if np_dtype is None or not hasattr(value, 'size'): return False return True #============================================================================== # Sorting #============================================================================== def sort_against(list1, list2, reverse=False, sort_key=None): """ Arrange items of list1 in the same order as sorted(list2). In other words, apply to list1 the permutation which takes list2 to sorted(list2, reverse). """ if sort_key is None: key = lambda x: x[0] else: key = lambda x: sort_key(x[0]) try: return [item for _, item in sorted(zip(list2, list1), key=key, reverse=reverse)] except: return list1 def unsorted_unique(lista): """Removes duplicates from lista neglecting its initial ordering""" return list(set(lista)) #============================================================================== # Display <--> Value #============================================================================== def default_display(value, with_module=True): """Default display for unknown objects.""" object_type = type(value) try: name = object_type.__name__ module = object_type.__module__ # Classes correspond to new types if name == 'type': name = 'class' if with_module: if name == 'module': return value.__name__ + ' module' if module == 'builtins': return name + ' object' return name + ' object of ' + module + ' module' return name except Exception: type_str = str(object_type) return type_str[1:-1] def collections_display(value, level): """Display for collections (i.e. list, set, tuple and dict).""" is_dict = isinstance(value, dict) is_set = isinstance(value, set) # Get elements if is_dict: elements = iter(value.items()) else: elements = value # Truncate values truncate = False if level == 1 and len(value) > 10: elements = islice(elements, 10) if is_dict or is_set else value[:10] truncate = True elif level == 2 and len(value) > 5: elements = islice(elements, 5) if is_dict or is_set else value[:5] truncate = True # Get display of each element if level <= 2: if is_dict: displays = [value_to_display(k, level=level) + ':' + value_to_display(v, level=level) for (k, v) in list(elements)] else: displays = [value_to_display(e, level=level) for e in elements] if truncate: displays.append('...') display = ', '.join(displays) else: display = '...' # Return display if is_dict: display = '{' + display + '}' elif isinstance(value, list): display = '[' + display + ']' elif isinstance(value, set): display = '{' + display + '}' else: display = '(' + display + ')' return display def value_to_display(value, minmax=False, level=0): """Convert value for display purpose""" # To save current Numpy printoptions np_printoptions = FakeObject numeric_numpy_types = get_numeric_numpy_types() try: if np.ndarray is not FakeObject: # Save printoptions np_printoptions = np.get_printoptions() # Set max number of elements to show for Numpy arrays # in our display np.set_printoptions(threshold=10) if isinstance(value, np.recarray): if level == 0: fields = value.names display = 'Field names: ' + ', '.join(fields) else: display = 'Recarray' elif isinstance(value, np.ma.MaskedArray): display = 'Masked array' elif isinstance(value, np.ndarray): if level == 0: if minmax: try: display = 'Min: %r\nMax: %r' % (value.min(), value.max()) except (TypeError, ValueError): if value.dtype.type in numeric_numpy_types: display = str(value) else: display = default_display(value) elif value.dtype.type in numeric_numpy_types: display = str(value) else: display = default_display(value) else: display = 'Numpy array' elif any([type(value) == t for t in [list, set, tuple, dict]]): display = collections_display(value, level+1) elif isinstance(value, PIL.Image.Image): if level == 0: display = '%s Mode: %s' % (address(value), value.mode) else: display = 'Image' elif isinstance(value, pd.DataFrame): if level == 0: cols = value.columns cols = [str(c) for c in cols] display = 'Column names: ' + ', '.join(list(cols)) else: display = 'Dataframe' elif isinstance(value, bs4.element.NavigableString): # Fixes Issue 2448 display = str(value) if level > 0: display = "'" + display + "'" elif isinstance(value, pd.Index): if level == 0: try: display = value._summary() except AttributeError: display = value.summary() else: display = 'Index' elif isinstance(value, bytes): # We don't apply this to classes that extend string types # See issue 5636 if type(value) in [str, bytes]: try: display = str(value, 'utf8') if level > 0: display = "'" + display + "'" except: display = value if level > 0: display = b"'" + display + b"'" else: display = default_display(value) elif isinstance(value, str): # We don't apply this to classes that extend string types # See issue 5636 if type(value) in [str, bytes]: display = value if level > 0: display = "'" + display + "'" else: display = default_display(value) elif (isinstance(value, datetime.date) or isinstance(value, datetime.timedelta)): display = str(value) elif (isinstance(value, (int, float, complex)) or isinstance(value, bool) or isinstance(value, numeric_numpy_types)): display = repr(value) else: if level == 0: display = default_display(value) else: display = default_display(value, with_module=False) except Exception: display = default_display(value) # Truncate display at 70 chars to avoid freezing Spyder # because of large displays if len(display) > 70: if isinstance(display, bytes): ellipses = b' ...' else: ellipses = ' ...' display = display[:70].rstrip() + ellipses # Restore Numpy printoptions if np_printoptions is not FakeObject: np.set_printoptions(**np_printoptions) return display def display_to_value(value, default_value, ignore_errors=True): """Convert back to value""" from qtpy.compat import from_qvariant value = from_qvariant(value, str) try: np_dtype = get_numpy_dtype(default_value) if isinstance(default_value, bool): # We must test for boolean before NumPy data types # because `bool` class derives from `int` class try: value = bool(float(value)) except ValueError: value = value.lower() == "true" elif np_dtype is not None: if 'complex' in str(type(default_value)): value = np_dtype(complex(value)) else: value = np_dtype(value) elif isinstance(default_value, bytes): value = bytes(value, 'utf-8') elif isinstance(default_value, str): value = str(value) elif isinstance(default_value, complex): value = complex(value) elif isinstance(default_value, float): value = float(value) elif isinstance(default_value, int): try: value = int(value) except ValueError: value = float(value) elif isinstance(default_value, datetime.datetime): value = datestr_to_datetime(value) elif isinstance(default_value, datetime.date): value = datestr_to_datetime(value).date() elif isinstance(default_value, datetime.timedelta): value = str_to_timedelta(value) elif ignore_errors: value = try_to_eval(value) else: value = eval(value) except (ValueError, SyntaxError): if ignore_errors: value = try_to_eval(value) else: return default_value return value # ============================================================================= # Types # ============================================================================= def get_type_string(item): """Return type string of an object.""" # The try/except is necessary to fix spyder-ide/spyder#19516. try: # Numpy objects (don't change the order!) if isinstance(item, np.ma.MaskedArray): return "MaskedArray" if isinstance(item, np.matrix): return "Matrix" if isinstance(item, np.ndarray): return "NDArray" # Pandas objects if isinstance(item, pd.DataFrame): return "DataFrame" if isinstance(item, pd.Index): return type(item).__name__ if isinstance(item, pd.Series): return "Series" except Exception: pass found = re.findall(r"<(?:type|class) '(\S*)'>", str(type(item))) if found: if found[0] == 'type': return 'class' return found[0] else: return 'Unknown' def is_known_type(item): """Return True if object has a known type""" # Unfortunately, the masked array case is specific return (isinstance(item, np.ma.MaskedArray) or get_type_string(item) != 'Unknown') def get_human_readable_type(item): """Return human-readable type string of an item""" # The try/except is necessary to fix spyder-ide/spyder#19516. try: if isinstance(item, (np.ndarray, np.ma.MaskedArray)): return u'Array of ' + item.dtype.name elif isinstance(item, PIL.Image.Image): return "Image" else: text = get_type_string(item) return text[text.find('.')+1:] except Exception: return 'Unknown' #============================================================================== # Globals filter: filter namespace dictionaries (to be edited in # CollectionsEditor) #============================================================================== def is_supported(value, check_all=False, filters=None, iterate=False): """Return True if value is supported, False otherwise.""" assert filters is not None if value is None: return True if is_callable_or_module(value): return True elif not is_editable_type(value): return False elif not isinstance(value, filters): return False elif iterate: if isinstance(value, (list, tuple, set)): valid_count = 0 for val in value: if is_supported(val, filters=filters, iterate=check_all): valid_count += 1 if not check_all: break return valid_count > 0 elif isinstance(value, dict): for key, val in list(value.items()): if not is_supported(key, filters=filters, iterate=check_all) \ or not is_supported(val, filters=filters, iterate=check_all): return False if not check_all: break return True def is_callable_or_module(value): """Return True if value is a callable or module, False otherwise.""" try: callable_or_module = callable(value) or inspect.ismodule(value) except Exception: callable_or_module = False return callable_or_module def globalsfilter(input_dict, check_all=False, filters=None, exclude_private=None, exclude_capitalized=None, exclude_uppercase=None, exclude_unsupported=None, excluded_names=None, exclude_callables_and_modules=None, filter_on=True): """Keep objects in namespace view according to different criteria.""" output_dict = {} def _is_string(obj): return type(obj) in [str, bytes] for key, value in list(input_dict.items()): excluded = ( (exclude_private and _is_string(key) and key.startswith('_')) or (exclude_capitalized and _is_string(key) and key[0].isupper()) or (exclude_uppercase and _is_string(key) and key.isupper() and len(key) > 1 and not key[1:].isdigit()) or (key in excluded_names) or (exclude_callables_and_modules and is_callable_or_module(value)) or (exclude_unsupported and not is_supported(value, check_all=check_all, filters=filters)) ) and filter_on if not excluded: output_dict[key] = value return output_dict #============================================================================== # Create view to be displayed by NamespaceBrowser #============================================================================== REMOTE_SETTINGS = ('check_all', 'exclude_private', 'exclude_uppercase', 'exclude_capitalized', 'exclude_unsupported', 'excluded_names', 'minmax', 'show_callable_attributes', 'show_special_attributes', 'exclude_callables_and_modules', 'filter_on') def get_supported_types(): """ Return a dictionnary containing types lists supported by the namespace browser. Note: If you update this list, don't forget to update variablexplorer.rst in spyder-docs """ from datetime import date, timedelta editable_types = [int, float, complex, list, set, dict, tuple, date, timedelta, str] try: from numpy import ndarray, matrix, generic editable_types += [ndarray, matrix, generic] except: pass try: from pandas import DataFrame, Series, Index editable_types += [DataFrame, Series, Index] except: pass picklable_types = editable_types[:] try: from PIL import Image editable_types.append(Image.Image) except: pass return dict(picklable=picklable_types, editable=editable_types) def get_remote_data(data, settings, mode, more_excluded_names=None): """ Return globals according to filter described in *settings*: * data: data to be filtered (dictionary) * settings: variable explorer settings (dictionary) * mode (string): 'editable' or 'picklable' * more_excluded_names: additional excluded names (list) """ supported_types = get_supported_types() assert mode in list(supported_types.keys()) excluded_names = list(settings['excluded_names']) if more_excluded_names is not None: excluded_names += more_excluded_names return globalsfilter( data, check_all=settings['check_all'], filters=tuple(supported_types[mode]), exclude_private=settings['exclude_private'], exclude_uppercase=settings['exclude_uppercase'], exclude_capitalized=settings['exclude_capitalized'], exclude_unsupported=settings['exclude_unsupported'], exclude_callables_and_modules=settings['exclude_callables_and_modules'], excluded_names=excluded_names, filter_on=settings['filter_on']) def make_remote_view(data, settings, more_excluded_names=None): """ Make a remote view of dictionary *data* -> globals explorer """ data = get_remote_data(data, settings, mode='editable', more_excluded_names=more_excluded_names) remote = {} for key, value in list(data.items()): view = value_to_display(value, minmax=settings['minmax']) remote[key] = { 'type': get_human_readable_type(value), 'size': get_size(value), 'view': view, 'python_type': get_type_string(value), 'numpy_type': get_numpy_type_string(value) } return remote spyder-kernels-3.0.3/spyder_kernels/utils/pythonenv.py000066400000000000000000000054351475072611100232740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Utilities to get information about Python environments.""" # Standard library imports from __future__ import annotations import os from pathlib import Path from typing import TypedDict class PythonEnvType: """Enum with the different types of Python environments we can detect.""" Conda = "conda" PyEnv = "pyenv" Custom = "custom" # Nor Conda or Pyenv class PythonEnvInfo(TypedDict): """Schema for Python environment information.""" path: str env_type: PythonEnvType name: str python_version: str # These keys are necessary to build the console banner in Spyder ipython_version: str sys_version: str def add_quotes(path): """Return quotes if needed for spaces on path.""" quotes = '"' if ' ' in path and '"' not in path else '' return '{quotes}{path}{quotes}'.format(quotes=quotes, path=path) def get_conda_env_path(pyexec, quote=False): """ Return the full path to the conda environment from a given python executable. If `quote` is True, then quotes are added if spaces are found in the path. """ pyexec = pyexec.replace('\\', '/') if os.name == 'nt': conda_env = os.path.dirname(pyexec) else: conda_env = os.path.dirname(os.path.dirname(pyexec)) if quote: conda_env = add_quotes(conda_env) return conda_env def is_conda_env(prefix=None, pyexec=None): """Check if prefix or python executable are in a conda environment.""" if pyexec is not None: pyexec = pyexec.replace('\\', '/') if (prefix is None and pyexec is None) or (prefix and pyexec): raise ValueError('Only `prefix` or `pyexec` should be provided!') if pyexec and prefix is None: prefix = get_conda_env_path(pyexec).replace('\\', '/') return os.path.exists(os.path.join(prefix, 'conda-meta')) def is_pyenv_env(pyexec): """Check if a python executable is a Pyenv environment.""" path = Path(pyexec) return "pyenv" in path.parts[:-1] def get_env_dir(interpreter, only_dir=False): """Get the environment directory from the interpreter executable.""" path = Path(interpreter) if os.name == 'nt': # This is enough for Conda and Pyenv envs env_dir = path.parent # This is necessary for envs created with `python -m venv` if env_dir.parts[-1].lower() == "scripts": env_dir = path.parents[1] else: env_dir = path.parents[1] return env_dir.parts[-1] if only_dir else str(env_dir) spyder-kernels-3.0.3/spyder_kernels/utils/style.py000066400000000000000000000077551475072611100224110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """ Style for IPython Console """ # Third party imports from pygments.style import Style from pygments.token import ( Name, Keyword, Comment, String, Number, Punctuation, Operator, ) def create_pygments_dict(color_scheme_dict): """ Create a dictionary that saves the given color scheme as a Pygments style. """ def give_font_weight(is_bold): if is_bold: return "bold" else: return "" def give_font_style(is_italic): if is_italic: return "italic" else: return "" color_scheme = color_scheme_dict fon_c, fon_fw, fon_fs = color_scheme["normal"] font_color = fon_c font_font_weight = give_font_weight(fon_fw) font_font_style = give_font_style(fon_fs) key_c, key_fw, key_fs = color_scheme["keyword"] keyword_color = key_c keyword_font_weight = give_font_weight(key_fw) keyword_font_style = give_font_style(key_fs) bui_c, bui_fw, bui_fs = color_scheme["builtin"] builtin_color = bui_c builtin_font_weight = give_font_weight(bui_fw) builtin_font_style = give_font_style(bui_fs) str_c, str_fw, str_fs = color_scheme["string"] string_color = str_c string_font_weight = give_font_weight(str_fw) string_font_style = give_font_style(str_fs) num_c, num_fw, num_fs = color_scheme["number"] number_color = num_c number_font_weight = give_font_weight(num_fw) number_font_style = give_font_style(num_fs) com_c, com_fw, com_fs = color_scheme["comment"] comment_color = com_c comment_font_weight = give_font_weight(com_fw) comment_font_style = give_font_style(com_fs) def_c, def_fw, def_fs = color_scheme["definition"] definition_color = def_c definition_font_weight = give_font_weight(def_fw) definition_font_style = give_font_style(def_fs) ins_c, ins_fw, ins_fs = color_scheme["instance"] instance_color = ins_c instance_font_weight = give_font_weight(ins_fw) instance_font_style = give_font_style(ins_fs) font_token = font_font_style + " " + font_font_weight + " " + font_color definition_token = ( definition_font_style + " " + definition_font_weight + " " + definition_color ) builtin_token = ( builtin_font_style + " " + builtin_font_weight + " " + builtin_color ) instance_token = ( instance_font_style + " " + instance_font_weight + " " + instance_color ) keyword_token = ( keyword_font_style + " " + keyword_font_weight + " " + keyword_color ) comment_token = ( comment_font_style + " " + comment_font_weight + " " + comment_color ) string_token = ( string_font_style + " " + string_font_weight + " " + string_color ) number_token = ( number_font_style + " " + number_font_weight + " " + number_color ) syntax_style_dic = { Name: font_token.strip(), Name.Class: definition_token.strip(), Name.Function: definition_token.strip(), Name.Builtin: builtin_token.strip(), Name.Builtin.Pseudo: instance_token.strip(), Keyword: keyword_token.strip(), Keyword.Type: builtin_token.strip(), Comment: comment_token.strip(), String: string_token.strip(), Number: number_token.strip(), Punctuation: font_token.strip(), Operator.Word: keyword_token.strip(), } return syntax_style_dic def create_style_class(color_scheme_dict): """Create a Pygments Style class with the given color scheme.""" class StyleClass(Style): default_style = "" styles = create_pygments_dict(color_scheme_dict) return StyleClass spyder-kernels-3.0.3/spyder_kernels/utils/test_utils.py000066400000000000000000000026211475072611100234330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2018- Spyder Kernels Contributors # Taken from the tests utils in the Metakernel package # See utils.py at https://github.com/Calysto/metakernel/metakernel/tests # Licensed under the terms of the BSD License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- try: from jupyter_client import session as ss except ImportError: from IPython.kernel.zmq import session as ss import zmq import logging try: from StringIO import StringIO except ImportError: from io import StringIO from spyder_kernels.console.kernel import SpyderKernel def get_kernel(kernel_class=SpyderKernel): """Get an instance of a kernel with the kernel class given.""" log = logging.getLogger('test') log.setLevel(logging.DEBUG) for hdlr in log.handlers: log.removeHandler(hdlr) hdlr = logging.StreamHandler(StringIO()) hdlr.setLevel(logging.DEBUG) log.addHandler(hdlr) context = zmq.Context.instance() iopub_socket = context.socket(zmq.PUB) kernel = kernel_class(session=ss.Session(), iopub_socket=iopub_socket, log=log) return kernel def get_log_text(kernel): """Get the log of the given kernel.""" return kernel.log.handlers[0].stream.getvalue() spyder-kernels-3.0.3/spyder_kernels/utils/tests/000077500000000000000000000000001475072611100220235ustar00rootroot00000000000000spyder-kernels-3.0.3/spyder_kernels/utils/tests/__init__.py000066400000000000000000000005271475072611100241400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright (c) 2009- Spyder Kernels Contributors # # Licensed under the terms of the MIT License # (see spyder_kernels/__init__.py for details) # ----------------------------------------------------------------------------- """Tests.""" spyder-kernels-3.0.3/spyder_kernels/utils/tests/data.dcm000066400000000000000000004164231475072611100234330ustar00rootroot00000000000000DICMULÐOBUI1.2.840.10008.5.1.4.1.1.2UI>1.2.392.200036.9123.100.11.15002200303521616157144551003340153UI1.2.840.10008.1.2.4.90UI1.2.276.0.7230010.3.0.3.5.4SHOFFIS_DCMTK_354 CSISO 2022 IR 13\ISO 2022 IR 87 CSORIGINAL\PRIMARY\AXIAL\NORMAL DA20191019TM 093829.09 UI(1.2.392.200036.9123.100.11.12.700006008UI1.2.840.10008.5.1.4.1.1.2UI>1.2.392.200036.9123.100.11.15002200303521616157144551003340153 DA20191019!DA20191019"DA20191019#DA201910190TM 093431.70 1TM 093820.48 2TM 093606.75 3TM 093829.98 PSH`CSCTpLO€LOSTPNSH0LO>LOLv2 @LOPPN`PNpPNLOSupria UNHMC UNHMC - CT - ID UN˜1122820191019HDL10005Supria N 00020000‚Ó‚½‚Εa‰@ ¼À½´ ÉÌÞº ¼À½´^ÉÌÞº 194211150076F 2019101909360675 HFS A H +00032.4-022.0+035.0 0000012002305.0002.000000000B L RLAPÜÜè7nÜ>7nÜ>€À05120011 001 0 1 0 1 1 0 0  Lv2 00000000000049000000000000040000000000020000000000072000-00340.0(d(È(dA 0001N I 4 x°] I:\Data\11228\0000000000004900\000000000004\0007.cti ¾° 1 0000000000000 UNh 20191019093431702019101909382048 “ª•” HEAD Lv2 2019101909360675.6255.008 49 +00032.35569-0.126001.2.392.200036.9123.100.11.150022003035216161571445272033398511.2.392.200036.9123.100.11.150022003035216161571445500033401461.2.392.200036.9123.100.11.15002200303521616157144551003340153000000000.000+0000155.00000000000000000000000000000000000000000F201910190938299800è€À1.2.392.200036.9123.100.11.12.700006008.3.2019101909343170.1 6008 000000.00000000000.00000C 2 0186 +00019.0 +043.0007nÜ>7nÜ>è @+0000019.000 @O 1.2.840.10008.5.1.4.1.1.2 700006000 0000 L 0  1 ®8B®8D0 0000.00000.0 SERVER  @ PN JXD191021006 LO JXD1910210060DA2TM@CSAS076Y DS0DS@LTLOCSHEADPDS5.00`DS120 DS220 LO 700006008  LO00050LO194 ADSBTMCTMFDSDS220  DS-22.0 0DS-340.0@CSCWPIS2000QIS230 RIS460 SH11QCSHFS UN SET WINDOWUN00UNè UI>1.2.392.200036.9123.100.11.15002200303521616157144527203339851 UI>1.2.392.200036.9123.100.11.15002200303521616157144550003340146 SH4900 IS4 IS2 IS7 CSL\PF 2DS-110.2153\-98.1898\72.1446 7DS*1.0000\0.0000\0.0000\0.0000\0.9272\-0.3746 RUI<1.2.392.200036.9123.100.11.12.700006008.3.2019101909343170.1 @LO ADS32.3557 @LT(US(CS MONOCHROME2 (US(US(0DS 0.431\0.431 (US(US (US (US(PDS40\40\40(QDS 100\100\200 (RDS0 (SDS1 23LOàOBÿÿÿÿþÿàþÿà–ÿOÿQ) ÿd#Creator: JasPer Version 1.701.0ÿR ÿ\@hppxppxppxppxppxÿ ÿ“ßø X(°,["=ÅmØ ffÄ7b}Ž£H£²™Íâ/Ë+Ø6²ãÿ°ÓŸ-·c'@äoœ ê™Pºîôü¶Äý ͈'ü_K¹_~ÐõðM>øPû‹uéh·X\ôwý̺/§ç”ñ”šgy#AOŠòÿlùýéw€³‘äë±ÈŠOak m“äÆU—$=?‚;÷ñ?øŒp{œú~JÎN0 5éôϳÖ{RcwÀwÚÉ“p¾ö¢å:"¡59iا¨L{IßY‹ Öž‚=0l0ù-ŽÃôÕ_VÕ夕ñ]ÛkuvéTq›%\ÏÉäqEÝõŒšb<œV…~#Í™PÇÙÿf ±ÌD„>wÖÂ=^9§h‰¥½ñ`øÖÁs‚‡æ%õÖÃH­^Pö¿™|¯û¡ONœvK%òšÁzŠ@ ÿj(cž«Ü_—VG¡ê tWX±ÒÍF‘€·øe ãX%»p?Mõê#$#‹Ý£ó¯èÎdÈÌ–k;â¾ú¾Œ¯qWd»€ì«Ç 3.2ÛdŒ=0wÙ&ÃÍwX”Û?Ïüw¿ðuÚàì(3k°š|b¾g=¶-þÔ8·?MÏ“*Õ{Þz'‚™ã>ôýõ3Hr®—Ï5–æn³ÓÉèåPΖ±'æ/g¡)ùa²Øe¯k¨”ùȯ’Äœ0Õ9óÞ — cã¼°ëì>2n°ºD“È@…0Iî¥;ðaj„ĺ þ3'—mžÝ6P½Izn^x>éäbüO´4ö¡8ùé]®Ç3%Ï7J/;eXÀ<§â;…!DnƒÏ4~ÑãÞÐg‘¥7 =äÁÒE2qÑ@Ò“©-¦ãʪ+›5Ÿ} îÕ(B šÆ¦,`Ç>TÄŠ<‘`8–Ø1¤ß fK΄OÈ&?w…ãÐ ܈1™¬æÊ™úì+ÿ_ÒšÉÍåŒõ‚a:ó—ŸøR£3„<2`ÛÝlÁ¦³ó?.Hï–±E}ˆxêœaæ¤MÄ f\ýoÛ›~ƒ ÈéÔÁˆ*ÊÉVUw½Ý‡¢Þ› JW#Ü‚¼+ªê4›Fðz1TÇý€4ñ<ø£‡¼“NK.¬¾z7<ñÂmõ¡|ð›)ì‰Ùá mꦞñ»Çm ¥Lè8…3Û[EÉô¶dÄ›òYÖÔAãŽÔ\D°P¿—½:N–z²sãçüémQ³Ã,gzõäØÀüyP{‚Â"& ä3[­¦WßOòõA_$ú†½þ Óâí뇖5:_®=ïq»é!sÓ~ ›_Ìž¡¡N2:{-¤ q€Ñy9Ç2ÿj+’müQeuúÙ úÌTÑxg•ðÕN,ð=EÚCºw0YÕéT¯fÐܾEK7VHÛ0cÒ=MV¢t-ãÓ)ªÈÃgÃ(jrµa›3•_¶\ ”D¬üÍУ›™ÊÙv@×5lw„ÌL9âa{OßAJA™žÕÌø1• ´Ù‘>ò@‹êêeÞ½V9‡v£owî 9¹‚ó­TF…7³"I÷É¿­®'vô5µ!W]W<¦ÔLš8UÆ×ŸÍuÍ”*g°Çx²øÿv#Ý9’(ÌŽ5uèsGå`æ–ˆK—Îá/™®7@à8øÎú´@sB´w±Fñ‡tô“iT”"5Ë üÝ•"B“—‘ šp£CZ¹'-LÓ@¤»!ís¶‡¥Š*k1îQKo§úMïK_` ˆYéÔùÞòNI™à£èfë! .}oв {™ G2פïu…E «m¦3—%Átâí]™ŠÅ"Mé!>›K’ Í d«Ê{N¯åÄ;21ÕÜ[r¸´‰1&•½ëÙ79|ƒ´rk–šÍ'÷ú§kã)¨Ê®]»…sãvÝê?ÊÿA……zU=ëßø>šwþ¦Œÿ`úw€Ó¬èÌËÏÿX$™À`±Aùˆ)^¢‚…è¨Gô1êÏ*…ØŒC~£_h·m+/zª£Ö/(uÚe)S’Ò%WqÁP@ûÊ·„c„Çý£%R„ŒÜR ?ÕçRºÃÒÁ†¯¢*C¤‚?kfoaˆ7mOô8C¡G^TLpGijs°{(¿GÏMÝõ•<Àê5¿­þè²ÈÓv X9؄⬖$¡üÆ2í·ØérlUÜß>Ì$8`ª½m>8'\û··bŽí ¼á;k§DÍ6xŠéЪöw¸Þ ðX§*ûªŠ\ ªü+,:×€‹«wÞ¯Ý?½->áø¿,Œt«Ê'.Aµ}eЃ/'e‘ø>]Q¢p¢£/ˆ=Õ1ɲœ3ô½) Pä½iÉOÄþ”‰(èú0OêBGdtÁÎ.M²J¶/2X¬]ˆÖg#2Žà5Ûæú{­Ž\B+L8Ç{!`¼]ÿË6DûM¥žNUrX€ÿbý\­ šò5$Шò÷ÑüÙ­WÜÕ ûõwZ6ž0ðâ*âãïzhC{8Ojf \¡ÀÂi[j³nõž„°·â…à‚éc[—ô?õR¤Gû*úJ¾5¬yîq©vÎõ§™°(h\Öõ䦑ri j,;‰4ƒÙß;ìÛGüéu/=û‹ù·f"‚W­Äò$ÞX3%h¡†ô7”1w·‡ßû ¥¥L ·ÉϸD¢ ßæaÛT!Ha2ÏHÞû žrÚÅžþýi«êµ:løß[0" y®Ï[¯ÊªQ¥é¡-yG]TÛÿ~_ÿIS`Öü'2ìí7(fBüà¿áDýúýQ¸U@ nüé¼ÛÁŒ…P'ËHM_ø ŽœŒˆœ»š| ç­À eJSx/ÌDü)ôu«ßê;e*¦G­ò[0y†J‚Só£W( 9`ÕCXòÀÑ›÷ßSÍpùsWnT…û»¿NG¼&ÙÙÇNµ×äž6DjQÀç3™´W»F^Y`%øÕ.zú›¿v-§§þî×?—]oÈtê‹Øš–Q€Ñ8(°!ô_|¹þİѬQÌP· ?XgÙ4O ç è½?oµµ\\5T‹dx©Ç`½¡Ž¢Lð¼eË{ZIêDG?5ÖìPßiì«äuü :ûŽê¥„ÌC¯ÒÄð•¡·ZÓÝÌå?Zçí}®¿Bòþ.…Àl• WwŸNF!¾ÁÍ^ãì;=|^0Ê_ …½Œ‡¬û<÷“¦FvZÃI…u‹ÜtñŒ3jNÖ¹ï\(•ÞAÿ8§eÔ÷K›¦Û_iÚ¾8"pN¸†¤šk^÷ÿ8åˆ&]Öw§ªÌySf]¾gò´A1? "/áà$3'l{>è5±BS‡Ä[ bS‘72›ç·øf÷+9¶Ó4ˆ˜F¯òú3,f¸tuV!·m´¼Fô¾ †òŒC»ƒóëd)žnnþz5ÊÏ.h·#‡ŽÖ4ýâ_H2ßë,6ëéý}c-Û»Q&óX¨ègÉ•í#B1v•ƒ/Ýa= ± +n‚*¤—ÏN•¯t†mÌÙ¤ÿTªYõëâ|Buñ'÷ö‰ÑŽOáj½=Ý ùÌ1¸p|­tP ]˜+¥o©w®Ú”†¶»ÏÒG¸µ÷?™^r]IãñŽpí¡¬È ŸWëʶ `#A@’51ÝÒó@I<‡´ÏšE¸ZÀù9ÂjHôq#²]ù’9uùC ïâSÌIñÎÄ&_jBŽ»ú²xDÀ2šF$ÕJkÓcݲ"³ ,=9þzѼJ=üz±û4™~›œ¹så©L¬ ¶£®U#7×G<^í÷‹‹šÐWŠ‘þq°†{ª!õÉ•°¾]Š ¦Ö—ó¤0¼ÕO—6‚¤_Í8AÅé ßq7‹2 ¼úE^Ï(rz›¡e¹¼Æ¸Øà-2äJ¡VV1*_vCUfš?}&b¶'ùœ7ô’¯r‹#`õ®½enÂùW:î5Bn*¸ëä„q’?h´ §ŸÌçk²ÞQ¯^$µ‹W€ #ÿ >iåNJGÿk˜œÝ>e}ëuȹe4ôéàFâZpž¿4ÀwÇ*4gõrƒõ*n (è9Îþs$q­¿¡¬ªõ¨Ch(2©ñ d.Û,[>œÍ:~± ZÌJGãn Ž2Þ¥OÃ5ž"eŸùZ>EÖÝúðâ¹#i½%äÝG£‚øðc©ZÀW:Ò¯BndE·JTº"N}÷½0‡$â *`¶+©°§½qÚíƒ:¥BiPÙŠÊðâÈêáz'ò·{—6=úÆWÜ›d:¿¡Ôã2Ÿ~| ú¾¨è†¢$Ókè0Ôâlÿ š{]I›ÙojÄ Ð Y±NµzJe.íbê€Öæ@¡G<=@UíAõ‰ï¤syqt§Ûã²ÒUË`$BŠ ƒe Ýo@;ÅÂ•Ž²‡¯Ñû¸1f«Ì–i îFL$å#’´‡”²|^Ïä£ßä¬O°x¨ÞÍY­ëŠØ\hn.ó,‰þÒkK!£n¯=ü‹¶•L$ù6±«—¥H1å.ÌÜ9ü;G[À-ðãøšXlfÇ(¯}aéîŠDcS•Y'´ˆ÷÷­L™âņ˜¶~+ ɨlÄ9tîw j¸Zô2¿çú"J~W:VÊäP:9úaj—¡=ñŸ7è'ë–ÀËáý]Sð\¹wçˆ/W†Ãí҉̨ÿû× y;×lÑä:ʧâÈŘ7‚RXÍ«rêßóº ¡¢¦ßQÎÓÔí®XÃIÃqìèeª/-k« è>“(Ts}ïCì .Æ~¢_ÉðEÜÙEŒØêí“Y²ÎÜ'"¶ý1Ï(ƽŒd6®þ Éü-ECð˜p^Àúòªaa3­/Â:¾tŒ€„ùô­ê]'NRh’[(ô®ý6ÊŒK‹›Çsá~îß92™1¼´‹ «¡xFúãÁÈs6õYQ¯Ÿ½Ð(Tägª¯Dz½ÆŸ „„ÞD=%G$ƒýÄ·î=­üM°J[ñ©ÏƞͺÀš=o{*ÑIÝy£‚€n»CÏÛ\b5µRišrú¯2ÆW5ri½ÃZŸ ÿ-)e&¬®`3Œ6ǵ¿TZ{0yòØ/Y†^ß™ì¦uQ ÔNnÇÿP¥]ºí3*pÉjö+ȼâ`^²óàת‰Ó¦XêvZºéu¾ÍL‚>m¥ùíZíè/2ÖBã~… †ó ¨¥ª‘‚¦}—Ýá»ÿPÓ_áoöƒ}uѸfF~¼™?½å}NN„T§’x“@dQ6iKz88ï6o Nnœó÷TxoT²J8ªG’^fÛY;ð5›j|…#²t\*ÃE ájÑÔÿšÌ *p­ðïå>x;KeÞ´†Áູäú䀟‹jITQ+ƒ¨%_É_>†%ŸL‡q@ºåH2?à=UˆéO^e(×'î*;b± áξ£Ã¥ib£¡t»“ô²Ýâj »›,Õæ‡K1£É MÒ£µ×x?•€…w¦5×­Ûây]ìpÄ6œ¸TSš¬maÁ™_%ëŸê@?îÌå³n‰4%èpkzÔÔuV4×m°ÖWäm)ÇL"Y>”QaK~7FøC°ÔºÖüœ=ëËF©êÿ0WuÙCÜ-žªIY³ât+•Òd3©OüÐúÎÏJo’:Ph^g梼mÆâ>Ý*®§û’ƒ¾Mu+6öÔµhè²Ö"y9’®ÁàËå/˜ÜÅ@Oûpu®ý §8…\µz ï3Ü@U6ƒ'> %ñ9]¥˜\grcò:¶ÐìÛ.)kK^xÅ~¼¹5˜dbúÞp£š{~Ä))?Ö*}~zÐLH=†üäux·ŽàʾÂfÓ8Œ~Äü½ŽÅ^xw4hÌv™`bc_¢°` Tô%0$µ“JÏ' #¤cdø ('æå¶}®ŽøÒf8Û!^ºÜÍØÌ«ã QU]NûrÿlbKJHôŠŠÃË\YYµ®a¦¡µ py6o}mÇ»õå§ê©dí´lhk“_–v:ðwpR4˜L>PVïî±o¤!d”a;X¬ó’Ç.K›–ä‘Vi±É‡YÃ#]·‡äÆg/tòEæáwPÍN|V)¼L0½"à ú™î—D\XóÇ€ßø?{ÍÿAû×çþßdàlÿg¼Ânl ÈÏ-Ò-ݲ 6„Ñ»{QÜ¢*Ü™'£nìÐÈŸ2㌩{*cŒô\v³êJ‹Ìó°§³«’ósfÒ¼ÉÇ”Öåd @˜ÑÍ•dÖ;18âÕÈ2ÕNú»×l] ×þVæòH›û ±ZÏCÎb”É+ÍŠMj¤ºçÓÚœ¦Ö³ñ! sY¶<á’ý|žp!)¿Kˆÿ~#x¿,—}}ÅÐp’õ>ÛOÀÔÕlb•#ç͸e11®B*ý|¹ô6I1Ï«ò~R5Èé$*YÀ…^¦ÜŒIeÀ›ÎœJpÒ úg¼/Ï5£O)åV^¶‹WyÚi Å»þ3…]&ZÇ+i õçL\œÜ8›uS Ó¼U®Î9.˜ó qßÜù÷#Ô*[õçlB©Q¡öÒˆVtS bjQòü÷¥ÙSŒu¼ob–ïÑ &mDwÉPÕ ôsÞ÷{‚AÄ¢$3h“ÓC‡«g}7¶àÖíµÇÑÔRÙ©/”×gÈOAùgQT,™|ñpeÖ„(c™é‚ì–•ÅÆÏ[ÈÛÐ ñp'ñ³ ý¶^š¡³ÓܨCr©vÒÛcÌ®´ëˆî,QÅFWYŒ”·Ø&˜$ºnhxÖ…¶’ÝxTöošé™Üò/œ.E@¨le‹mõÓø\¹à:þ^µó!f;% …êL¤“`ÿ*ˆ|ëÎñã‹Uf›nÜ—¢áÈÍ­6FÐcqäÏ$Ž´Ëô2‚]FW3ªÒx‘Œž/H¬Î eg‘9G-P} º$üNðhâ"/7VýÙg·)³àú*þ†9=:¥CGqì÷ÍýŽÖð ÈXÈîuîõ’²™R)J–K¤îý^óHiâ¨me¿as%*íl%ŠËL.]w‚€;Óò¨ ™OìÊoëÌ3QÄ8DòSoA„éPxAo†²à¹:ŸkÏÆP°$^ÒŽí-‡‹Eo_%^-ãÀ€äwë:˜PRR/ÝÈË`sŒ³6U˜²Wï¹Ö‰Zô};¤Ú(5™'ð=£PóŒd§æ­§Š*%5ü0Zß!ß,Ôñã žB¥¹$ðA›òÊĬÓÉYgGÄ.õç­qh¦{q)-ŠRáh÷ÊDüU£Œ¯Oü†žXÜjÛ¦Ç!f·P¼ÛyÞÇ$á¸M:÷>£‚>}Û9» ÇÆóÄ(;ŽãÇ0aöæ 4Ëó³B•G ,Y†–½V™!.D³&Y|Eò8ÊXû<Öf7¥…rçg+¯.°¸²UdÒ1rÊqa †ÖÕzçÏ?‰¬9}3W^½õ‘>Ø[Oª¹‚v]\ÿNÑ2çG™ž*hoŽ×ðYqæ5øý7O2*„ŠŽ·¿^ÞšhÙòuÛŠ=ÊÞhŒÀr½9Ýi±lV€cÐŒ:;´YlÊÖ^k–›¿/XW£è†{uüv$ð"ÞŽ/eñãbX+ÿ0”=¦¿äáfv)Dz'—Â0B^¼vMÈÐ\EÚRöŸ,ÃÑåYIß3¿¯Q“p\Û««[€Fí <~hf ~b¶!½‘¾Ç°ÄC‰E„XUw›)³RHä€âÀê†Ïù/åyxfýÑ¡v®:Ze_K5£šOV O•)ç(vƒÁ¡Ë06ÍÎ@¼¾í#Œ(ƒÎv:$ËùôC:ÝD˜ã|pGQ£HR2xY ~tòT;·‰bæ]*ÕÒ¢³ÿJ â.p­À›Kó˜;P4Ñyò¡–¡È2}Qbœ;®À¾,‡\¡­Ë!.œóÄéedc^Ïõ}E/D†#Øã#±ù&üïß,Þ|>Š ¦’|G¼Ðá¢A~@4ì”ô`xmK…~Õ19t(h˜P3Nª…ˆ ¨©îÂÉükÒô2Æ<ô‹ë½blD}*ÁÔ/[c‰4 kf9ÕlLÚ3ˆjÆX0P.ë}›Û ÌÜ“ßââž—o7Y/,Œd™À²\÷—Ä@ú—˜•k9¿S'[Y‚¡#l`'Jè[^]µ(Äïî§°÷æÊ¬M¶Ø ÉmîêE§Ÿ:iwv†NTøì¬y(dSÛËŽžq‡ù¸|_ÍÀi6)ÃCqÐÇнݣyÞõf†Ø»î/”›<"ÎS°@IZ’ǘކX'á!ÿd,`¢8þNOTuSØÑH€.Qülíœf £qŒÓÞ4ÒÈÙ÷)ˆJy ìL¤ìØtÖ7ð Zvlá\ÖžŸW’kþF;è(ö6)EuÁ•;ÓöHýI‹Šrln*—h䔘¸­øâKŒ[—–™}ñž”8µL¡M³ª½°¶žÕ[{àç>}êì=}Àñ+9Öh†Ž„©ÖR1O&{ËA[¡ÇCkë ‚}S'ÿ „?CŸÇmɹ"@w¢Í9îGÛËJ¸ØÕ¨ùÙ£-Ö.ä©·Ên A별gΕɕ¶ƒ‘9>ŸXmÛ ^¨6(ZÒwûš@ ÷~Ô«çËLÏ>š¶f¿¹O©sYÈÏGØš»(h—Ú>í.¹I•Þ«ç)rÎË‹Qûr@ÅxZ…L³çõ°Ó¦yVÂPË‘jö[K+!lÍëg1%&‰Ÿ„šÔ"û`?—Á6# öÆ|5†•| ~gH6¤ázBfVÂD gäII}Að7ÝQ¼GJ ÍÇ ç@±ŸÑhk¥'ô½ÈÌ,¦Y;˸¾ö{:õ\â|ó϶|9ÂÚÂ~üKÝAW–Õ]Á,yL"Ñ»¶Ä±‰ÍÒšK·­=½zÏÇ•ü£êõ™Šo{KdWÚ_ ¶¹¸|f¾LòdNÝË«—lLÔŠPÀaâ³$ÊFv~ êô¢Ï¤=ü?Ï'o_ä¾Û-óFw?Í{Dûµöƒ ÞðÄa>ééûÁ«¯ˆš†¢½ú=SSªƒ×‡Ýp¶ÁHœòÛBºÏ¾= *‡.?„ ¨—€ q‚þS]b{2¤]Ëqýk8PÆL ¾~Ø©Z>âOyPãO)2†¨©bªïþôbB—×åO^«¿Y“vc,}ùëƒ=ø5W×4qˆcÀ¤™Ü}0ÚJðGCË–‡%B]ž›§hô¥¿üTê¶.;Xž´DXAÏ l¶Œ13çQË»)Ì xëÕ6 p=YI4>³\ Á^C/þs"Žf±Üƒšñ†“Â![âf%ÍœÃ?em·Hrdc ßZ*ÐãD4Âl vüØj“¥XÙ¥í)Ù:±Gß{ÈÈ€¡pßh˜æÐ¨ØO*x7*Nµ0ˆ´É†×£a7¾ˆjë¹âçíg§¾'†ØEÀ²¼ÖæODÇÀ{ß³é:¨$¿½Jb¡o6$öS‡`oo¬þs¶‚./Æa1ó’\o…­¾)å7~g"¼5úÓMÌv¡2&Mò jvWz… ~¤eWì¶H¤ZÕ©JÙšø®EÇñ–›^Y:W–­dÈø^AG ¿ÆÊC^ð ºráp„÷½o†‚½j‘.Ëa¢âuTúk†\µHˆ¨Çþ%íQÜÅô•™f¼Ú_pFÙh*‰Í Lòõü¸FÈÄ/þþçkÿ’ q }ß 5|Šæ3w­„B‹–¸ëUã¢ç˜þiŒ 2Ù.B2Öþ´,rN˜rž)KßàÓ5«–`–±8Ø3½å6GpDŸÉì2¬I´îÁëSBPÊxx±h|Ÿt+„C[¬‰ÜNwtsÓÔÍ‹a[ДÙ3ÞŸõå®SþÃJN±S¨AÒè=í'ºÎÉ`QâHLÃmËÊ—F5E&zDè;ÝÂRž†…!P!’ÌC^eoûðdå°DIm¨­²p£jb<%f¹Á•ÿxØfÿ0î ¼:Šñà¥M¥ºœ0áªM”¡´+ÏÓ<¶ž¥ü6 †éžé? ¡¯CBCQä§à, ´1Ñ RRк.UtèH<‚½Þnv=?\'`‡ipͯù:X:ôWi«’k­õ«ŒªÈ˵3În]¶q๾ô®Ä,…Ä¥S|ÿtc¥}pÖ¬"†¬•ïòíâŠäi™~®@`õ‰t.õï§ÏM§pCp¦ê^&´õã@]¯…[AH—6šÑ%1²›ë‘V&Äûƒ´” ê; ÿ'ãò z-±Ÿ@ú»Ç­Ùm4®‘bÞ¬yV7i™Àܼt¸µ'Iìƒ*«Rãs(6 T³¿GÕuñ¬­|ÖÖž€8Óñé?å9s ÿ>7ݵïW„Bd£´Ê1o×ø>ÖimçÕÊ`"õ1ªŽcܽ¥ù(/Îxò4TçIýŽ×Ùõ< äâô™´A¡¡†¬GA·UÀ^‹±¾Z“V¼åøž` ÅÈó^²VÕ<Ø}ÈDG‚í—QŽm2˲È;pêXûnf]ÀFQáã¡»ÑÑlöQ2HKV ×í-q(¶²2ÛmÔi§ØC}öÖÑM˲aÊØèÁŠR‡8÷9”¾U ¬Â@ã8A¾)Ò"ÔŸBŸ:™ìP,ã4Ïð»|å½,à ÖTòq{U‚º¢†ö6«ßÄ`¬œà ZÂæõ_}ѼZð;´¢ 7”,7NEÔù Ó)1d @ùÛ—ÛA¡êþƒ>5ÑN¿«\1ŒhŸ÷§¬Ï”QgÆönë,¿ØQôåêäÊ’lF3TŸqœûú‰¢Ÿô~ÄÓ`ò{ØeåîÖ‹YÒfskXH5$v™xGîtâóþ%Ê?@¿”\ŸêH íÝ|ûߢ4‘óŽS“OJf?‰å#Hºxê ,/U €º¾¨®â½5‘ÐT}çmþ&vft—ÿ…Q‘Ë1P Àó¥Ý¯HžÈöë' ›Y ‘³/·Ørm ˆV¶lW)®–×õ"À!Ë.â<ä]Æ[¥‘d³ÒÓ«Œ"ñ'8÷*ðúe’€i<®ÿ$H6½ài$gÖêžNa¼o$KÁdŽ·­èÁK9r©ã/ÒógBZôq6ïY°Ãâ]|öÝî~Kì;¥ß3êჯ̿Ô40{ËÓ¿š—ÿ!íÔ!) 1¥²XÒëpÅQãË[®Ê-ÄõÙæ[¥À „›Ýz&wRèƒprxtËyŒô¯­=ª±B|$ä!Ý šµghEív’ÚuÐoÐÝ“³ï^ç.µüÀjš>¹³XWkE{$[ Ö—c@Å—±Øð›6³,žÖ¯±n±(·šé'¦bae¢ïtÖ½#0$‘OÁÍò°oët‡<&…–ÄZ¸oÂóÇZ™ëû”Úýk€Mm8)óqN°²±„j·ÌסäÅu‹¯¼ÿuÅ­í\Ðö(Õq4«üéY#o„ÃÍMdUÚʧ̧V]{µ¸¬Œïšn¹´XÁ#ÜíõìÝõ3×ó«ÖY :PdZ5¢{Ñðl­Û×v¬Á[òÔëP˜@=)o,ˆ¿‘ÆñlA^²À iÆó`â€wBqDì®dá!EòÆN¿ºÞ7Û3¤h¼åœI==íPÓ—pêmGH9)K-ÆC,Ø/ŽQ4{s.ìÐwÂoœýG3ð^s X`A¤;=ù09Ôº7ôËõŠíÜ”dÊŸº4àg‚ê@2v»ö`²Ó¢¶u7_kC\¶º§f_Æ2pîÐЋË4 |VíyD Õ.=G­Ž>$ÆE¹I‡dŽŠq—Ò³—èaô¶ÿ(3ª@]±Æ øð²À0:„Lj¤ã¿½†‹1SÑMÅ„Xí‚Ú…ïfµÄs¼°} ¥fÿ.˜Ô4Fí³~BÎ ç?RNÜÎŽ§ú°Tã »‰Í¾ 7j‰š}±÷<˜4¿W˜ÓÖ#ë«á~ÓýI >…‘ ’ÖXÕÔ´ÈKu’u7ä3$8 v›eçÂ*1X9EÃà'ÊÕä&·¼8“@ ±aG¯~à1’£Úª™öK[i[¨ç‡ZkÈ+¦cáöSa›¦v©:ÉžÓÿ^)2 ¤ÍI"Ù¿ôò[ÔeÄ01wâo7g3W²«§ ›0œÂÆ.ˆ"Q«g}pó ÉŽSRŸ¼Aoª·…ÚÜïèüâ¶‹_°·¸bþ/=}xRëEã€gݨlî@soe`çßHj‚œ9D˜ Ú¯œÖÎ}ò>[C37ûÚŽO•4V÷$aò|›zÙì!²³GâYîFÖʵêsTËó Îû Ê‹O·Íô÷©þ›õšy»Ê¬¦/É_b—(Ëöý(Ï Y?ªÌ¯îZx¢ ÆZ!>±<œž|yD'in¨§<+C.‰ûqè2HÞ Ζè ѽ1E²#ª¬hïzî}eU:M#ÕŽœß ò9e›böÞËk%p Øt¹«q#>;Íx€Úñ&8Ex•_ ôº½í1;9Âü®Ä"æ&Þ$g/`­®Ö‡·&ËÌ"¤ÞEä9àЉÛ|doÞæ zxºüc–_¦°ùÆ’¤çrVÂpXN¯"¥¿ñª@KyÎ3F6¦µ×·¾V¿¯u%ºâw‹ Î=ýx™¤Ÿ m ë—E‹¿;:jV¯¥N‰r[ºÄ,±®z¼¡þŒ!5 •ñ| kšW5š/-àaN,úÄ” YEðÿp‹-á$©|“ÄØEˆ`·4Ì?Æ~Nßðô Ž#ÜÎñ©'šÞ ìRñÆaH@„g®§Vå }mh|áðêV®ø­ÈwÙ¿êNa8ÏI}ˆÁ*M)yÖdÒfA¦ÊBl¿æ[êÌÌzUE_˜ŽðëbÞä+k1s¼åÔ ] ‰}»…è^e6¼`§a†ñ‚Ð'§×=€ž€Àïæ~Ìg–¨T‚ãžëÅ«~jB®ÈÊßæ^êtÿ ·Mg†ƒ²dý/!FBÇØ õFÝ;=kØÑÄMÊTíë[¹êÀƒñº ߟ“!Ð…ƒÎsü5×@n»Lyë¢NŽÐýgšóAÅÍÉ ”‘KÑ!״⇘ts;CÍJ§OUCIö ‚Ýé øÉYÒƒéÌöùGçÞŠ‡/«ÞÜ0£-¤kÄ鈛Q”{ªD­–Z²«k$s#~¬è@ë;ÂsÖ1j¸«à0oÇ}30ÎLÄ×# Ò€ÈSQñÁN“û´uÂaþpñº†í>1¦(?@³&÷Ý–¯åìßYI‚¨ÃJ@Ìîü­ÿ [¤[‚ÁݸµsjÛJ]Ï R3Hp°’¨ „m愘ÌÄvAÅRýÇw¯ºû|×õ£CÅ÷—hªë¬W …šAîcœyµÇv}êN'!2u=:$E¡ïQ?Åÿ](Œ@G˜òâÆ`ÇÜóÆ)Øá¿áGák‰`˜Yh=½iªî)ocÉT˜T%/ÌÆbä~êùWŸ>ß8Á6‘yYÕ3%ŸXú:‘ ¢-½³ÉòÝ/¹KîkûÍû¨ÆU”|µ€Wüq§ü`’im”¡-@¯§ílâm1JüÁ³„²"7â!?à Çõg(±V`+$Åjjt—š~dƒbH^Î \ô<ÐP—;κ€íb OçåªØiûåHR¤1;‰É“!_&Q'øR¶Ze_TWãÐÛÖ‹0 Í 5…Êy¡£Wòa·â2¦¥´ S]uçOòI›fȨÈ^zAj1¢bÚî`s¸ˆQï?Ò'1ô ßHå]çÓ¥¨•e# ×mq…2ÝJv6—zQÕ ÒV%È™qEö‘ªë“ òzg(æÍÀ[+ÆŸ<¬Ì}÷tþ±Íü :‹_ sÑm<›îpŽ–dg«M'@4™LÙ~þëy/ñ—¡næ¶8ÓØaé~„¿ÈMÇúüE¤zäxbVÜiíÄ8˜p)CñqrZƒ7ãõû(J(>:¹Òê v×¾¾#O£­UZòñž¨‹F%ª(ŽÑ /¿Âa°©ì…äOh– =‡¸ü¦ÕâŠÛ:ù«´po²Ùü6…Ô @õÈx`°1;]¶©Ç'¥"ó9 o ÞpfY •òw7ÖÎøCÀ@›†D­L…Òh³Q@¾Cƒ×Yz8Q+MóN'{´’9å͸Ìu­ ø¹ZWv„vŠ{K½{ÐXV¼kt¸otÜAè7¾§”¼1ñ›—d,ÂWɧG–­ª{ðH ;$ñdTˆÔØÈNbv.‡ï~+dÁwÐunS~ÅËöÕÖçÇÄžeú=±YXYéh«à…fNöv‘ß%QqÓÂþêþo]øå aîµ<ƒ t‡/[D2ÚQC©QDpÁ2:Ê#$ø€ï,óS.6z*Êsízh°Ï(VÍËî%l{ éûÿC(’a“`’‡gKm,^ƒ¢¬…¸"‚UÕ™¥Mó.7ÁØ”â£6%"•zÖÅ«SœtÅ ÖrêÈŠ±¼ è;ÄÓIà¬)#ò»'zñ}iÈúÏÉ+𤠸·ë0kæhJh ²ŠáèÏÄ0H³3åÊÎK¹Á¤Ôh\ÎÒs‰!·’Ñ|¶žÚGIŠê7ñ6yLIWo¨dŒ@•?Û=¤¼Åw>u˜jÑíy-{—ºR‚@Cí}9ƒ´æU¸ciáV)~'0šõFPôAN h÷\ê¦Ís¥0í!LCŽ|`'±ˆþ÷V×M;@ñrLyâÐ,ȇFË5ÃÔOÌæt™b–|JêV$[.]Ue¹ÕêNèÿiü¯DZëõ« ÏkI,J!·]s¤N—¦|ø¤=‰ÅLQ¯B£ž7s¥ŽÐ€s;öuÆLÛ‹òé6L8ö<®¬p‰'”Íæ‰_Ê$“Î:fbVdhB3PQ⪮z[æYd }¼El ›2ý;ùÂÛXíkµ9 éœg¿ $@äÈW§œàŸª·˜’•3…È0Κ[ršÇC…鮾À „¾º@/NQ–ÿš*lp’OZt¯3Ç ZÐÖ¡²û<ÓÏnÈ®yk ¾j̦6^a*¤Ñ ·În•û¶¡+o=Ö/Q˜ºXïïGzDå͇A2DލQîðSfd—À¨!’U¥ÉÝãyœ×´ÿ`„‡ò“¶ICÍÁ†C)ŒÔÌç$h]ãŠ+/ñ„ô5æT\3„J®^Cjî(Úèg9‰V\×À ¢ÜÞgRN@¤˜ÑÇLse»"zÄpÀ Ñ…ùˆ4¸ÿl€=ÒRãOV~ÍQP3 KEÏŠõ®®wçhl„L\(¾‘]C ,aÕÇHÌ‘nqöÿPFN’ nŽ·’YŽuRNn@÷eé×¶^{¡ÛÜlÕªRÛ‘W2¯‹2‚M{ñºXÖ´ùěűt tÜgB©³ Úà¦ûisˆ™  @²<–‡©ß,h *…7  Å јۚ­àVßÞ\Ã(œhaèv &KäŠ~Á"æÕ»¦¬Ž¾dY#¼®Þ¿ÿÄ Á쟢6IÏãËOú2¢ÜqËß¿~‹`v݆øíA:ÔÇØ¨A»Pßö²K-ÓL½òëÅRÓ ÿ4iP„ZдW`Åê㙽oÃ/½á*€€ÿw}ØÄ×újÂKU‚)»2%þ»2Vú]óIÚºõÈéMÞf»äÉ›÷=œ«ãï‘kë҆Zº1í_¼6{»‰›jå<-8_¢øSû« ¿:}õKWzW “ß —`€  Ÿóxä®ÂÏT«¿0 ’Lê‚Þ@ð¹;©¹‰fWJÞ/A ü°°R>ÿBLN]V(î O`³ÏÌŸÖ#"O6”MJxý¶DêUSGk Óëbô‡š&’0«þ(¼ÀƒCçðóõd´—½ý;‚:^€ª•úÁ«/o”a’ÜçÜ•hþ(!4>¬ž$³’ÄEFf²ãËš"ùÁ㽬NŸYãÆó±˜¿0}Øl¹Ú`æY¬Ç({ÕJÍ…ò©“{‚ØLý£yýÅçÇk?ePÑèTÁTL:=jsgÊ«~ªÄÜ64!µÈ›»å0ã8l¿N`v?]“ê°DÒ^b#Ý8hÜ„½F†Xl¯M¢nã«n[2¶Uü³ù«‡Û'QÃß— ¯PKäÍfë…{£wìÍýœ_è~Ö[¬ 0@ã¾1¤ú¨EÐNdCÆlærͪ Œt_¨ªÃ>(–iœöëA| ¨&š\2þU2<…fk´æá/8í¯ZPolK§?ãû±WД?Æ{IÅ„ãz1ËŽ&_ft@õ|Ó©à·Í˜»{ë,?ÿeJÚt)øÔãè¸{”»&iaØhÌd—})Íâ`îS]^©4Åà« Í™q7SR:ÞOƒVžé¡wœM½kÃQ.ƒ‚æ i—€“ðYÌ7«7Ö,3Ò‡ ´Ä^¸‡dE»o ;™µç“ ëÊÔ5©å»í¨Ù—+>þnÔwà=ûéêÒÛLnä䀦*3zÈ?qdô¸ZÄ’ÐËïØKSp]J›} Ž–—ø>ø”eÖBãÔ0Czn 3Æí<‡¦h7Õ°üæÙÀ¢Zô8Œ\’¿¾ØøÛ\µÉáÀÛm_Ç€j¤mø›«àÕ7£`ÿÝ6N8À¼uŠh­. @XÔÚ­M=¬\šàsÜœ"E_uHÁq¢%j8s‘ÝɽÇ>LºÄ~uå{¯¢[ äµþòŒÀõßC% hmÏÚ_U£ ò–3ßœte»˜K‰T=4"*hn¢&R]ºULm8—Mˆuî ür­³‹ÕgÀ4ÔJ£'øá´‘)ÿãã×+uµÚi<ÒÁš1 0žöngQsk”i©®Vå9sÈ3&fÇ„“ÔûJÍ=L"楿ññõƒ:˜×Pç§,jâ RVoKÛ´R·ÿ>`pH•>aF§ËØæfÌ~pò°—ß§•ÿ\î<´{úP¢P6jD7Ü‹™d\9 AH%þÍëA}Z‰£eI‚pd‹½âô®}¡i¹}dŒjþµO"Å,; ÛÑhÃ7+œ˜´Á_&(¾W¥é9Lÿ õ˜¥Fú(ˆ…ƒtBA‹dõ‘†%î%¶M 8àb¾aBYÏ·¾.̤‹Nî*› ŒU3…ØmqšÈ’Û€‰×¢<äö‹.¡8¾ŒZýNšä*Omñè ¤zåµÞHÂäuýxê•ò(ªpŠ6ž½ñ»÷~QŽ´ë|6Ö˜Ìæõc²ÔEô©Èk¿ƒ„ÖñŸ”„a»¹WÅùã–37½’¥K0jó|sG»œEí4¬¾Ð¼À¿§õñ t.)öM+.¨§ÃdUŸøàæë‚%»`À¯‚ÜA¼q(1þôÛ¨*¨ÔÈ•qû)-îÚ9=G–FDïýS‹·’6åY!î„ÁªëNüù^ýÊùÔÃ!ë ù‘rcBM6˜7ò¨¾#…I  +hêU‹P×qÕÜP¦,žøÍ¦L§<‹=Ý· :ôOîÎã-X¦y¦zÛNYá½ •fIMÄO¤ ѨåÉ~c×ÒÇCÈ¢Ç+Ù—GHªç¹ª•¤l‰·YíÖÜŒ~¥é¦pdoóÑ¥G‘_ç÷Bïæú(e, q®"¯f|ÞÒÎÛò „[ÚÛŽì¿€†¯+¯}*B˜p¦Â3Ãñ°zZ{3WGUѳ eïõ"òå‘%`M™ÖÔòHΉ>oÁÜÕš“»L1mÿ_òçÔ(¯J©#ðYÐFwÕïÚR¸mðÛ8Ú휱"¬=“©™Xetrñ#¬«.ô SãæÁoIx&ÁŸ•]ë‹™ç¡êXPš~ÓñF|Ĉ„3b „‚Z¼WâÇô¶ÎQ§.×kû!¹3¥bï’ZÈ EUúï …#u1¹ 7<“ÈÊ&ŽÚ”Ò!0¦¢¾›îëo7:¯Ó!g߸â¨zO9Î5 ')NNaÔ²¿|jvÚÆWz4¥wåz³Ý=܃/?E¦ní©fÃ5ÚÐÑ¥_P\hõ"ˆëu’Å·Á_‘e!oÇ¿©´a§qDÄW] }ûsìC‹²·Úº_â[ÁÐéöJ§‘[Èg·í¸Ëwvz1ÓÞÀ +*BxÜXMÁWTØ9ÎP°]KÂDùJ ¢èc§ @¸CO닪j×±¼Çíob-„ÃA%UéÇçê~Ńïü#F¹g¡=Ší€­ð´jgÀÍÎÍAø“&Ù/¸í4ÿoZøûôÈîQt0F¼F¡?Øv¿ŒŽV—T¸<.\Ë#âþy} uÉ‘4ÇSØ‹6˯4*›ö¡‡ƒ’~ÚùeŠiã:*Hj·eÑ2åP/ üf4þ'•Ú‡þˆÙ#…'2êr¶«qhÖècsL(ŠO®vyÆ|ôh¼ü<оÊÞI­­÷Åwd;° j¨bWKÂÛ>6mýYZ´cò%þ0© `î…â‚w¢meo†|Ë÷¢¤Ê›EX 1†gu¿SX9ÅqUÝ è뻾oaöxÚ<ÇKèRåÚæ¾…;bÇ{tçk¡}&”ñB–á7÷5y*n™ Œ #ø…ƒ’YÇcŠ»àºïW"N oUGO Ä®î`ç|ÈÂNO &)EÉÉYg ·Í܇ø¾{(É*¯—‹|çbÀ3çœÉñuaÔô•O…u»Ò@Õš”ñQcî·%«ö2É@ÌòÉcÍoÖA昉~E0¾'¿´Á¸j!ÿË÷ƒ=QÕ[ÜkŒ5Liÿ$¾mvô2yX¸l‡(–/OÆ­º¤A¤Ü¯Ã7k]kê¿ìlß®è<•É¥4?µ±ÒÔœ>A^8¿Ó©ù¢jæ,¾Iä@©"L¹gvßhVÐ ÌqÔ¢vÙƒ6;ÝëÀúȪš»Ìö® “þ„6¨¡ã§Ì8‰ÿg³ãC-ÓIÉŸ %3pEÅÓ¤ÕGë¼—ûöµ“0r`” Ä3s1}ïßLOÐá=ÃÞ÷ P°n{ôòZêõ¹¶.ê8!]z‹™å–b²xFÉØG»éÆ»wÎÖ€j'ÝX蘭—r{‘HSÁ~‰u*ù‡Òt_bqÈ16pËð¾I› [9èˆ6Ï¥FWZ´#å´Jô¨!_ÃÇ y‡»Œ?Ÿ¨g¸QùgÛ +Ž´ÔÚp—sQ$~%ø9/œ\˜¾ ŽU¼¼°XÙ»9ÿPiÒÊGàzÄŒê­yðd )¨âbŒ¢¢"¢7O>÷LD‚6þ´Dk=™¦™e ‘#¾%ˆ¾AûEôð ˜_v–X½mmJeÖW>Zìiáºn@lã”õ—'¦V¢BÊ<ã1q qÏ”pNU¾ÔËœ[rŸ·më3‹Ze çQ»8²ãŸÐ:Àqw.ñ+æ8™º"1(”o:Š¢Ï«j_T›x‹MB#Šç¤ÌOȨ[D{ËPÇà^ùÙiÏnÜ\#;øðlž´›$+k®7B¡©ÖÏ{¢ÉL].ÖߊØò®‰1¨gô墕\ ¥(q-|ËNöpfmÀ©EíòM!“Š, ë4JO†õaÓì›fö²¶ÑŽbˆ²Íšèä}§ÕIŸ9‚3%³xrÉ}9ƒÖ‡ìQ"² ø‚'4Œ‹“p¬h9h©· ,æÆ³uÀ$øºé$dŽËSتðç$VÙ%NÀ#Ýe™÷¾Qq6\5È 0,Êü,4&ipÀñÈPø ¼ÈÁ9†¤•V·†›¨WÖ`iígØŽX‰_Ÿ&bAê…µ‡¸épM¶ÂK 2M8’ávôñ]ýÒ„˜:chðû”‚ǹ5tŧõ欗ßzµÐ"&!–`VàIû}ÓWÙÉžd=cœSé>d$õm#ý€'â=$×qmÉg$P1¹·L"`?¤ˆ»¡vU?W’vp·]Ôëÿ`ñx òƒƒŸÔN;úÈ"TN^¾‘2®Ñ+úAÍ/0&€‘C¯ g ÿrß…µ¢š0÷ð݇".Šd7ï_y}·uWßëÿõô_øYOÿökð~¿½ÿ`ýu×þÖÿ`ý–ü¯‡?ø?V_ÿõ ÿx?_—ÿõž€ãsAv'³ñLÌ·´K2$tDâì’@Ÿ6!‚~oX™rKœ1Ã}&‚꟪÷ !ˆ¹ƒÏÒLqR >y$´zN#È“£óE±ËÇ%.\gÚ¿‚Æ' #ß,a,èÈ5}.-ihÌi†TÑ S1ûI{rˆôô3éª;Ÿþ§šœõ/ND 2ø à¤0#”§-y?ü Pe鎬³¨ªï„û•x¸FŠÏàabyPzÃ’¦1ÕTùï'åo3:V;¹¬û¡‡Ó„&FŸ)Ðd}x¨_òéQJþî˜nmáçþ®%,eöøÄÖüLíS´:£·ÄuèöDïÝÂHQ:˜yZkÀ¿Ÿݨ§©³Â`Älb(mùWˆcãAZŵÕÐÁeaâÝ/ô!Äì½R“ó;„j‡Ú§îºõlc°¾¥cßž˜¹c¢ñx¯øç]Nè!N):¸eèÆiîçÊTÏWÜ&WÙd<}t!~±Ñe™µ›˜ŒCÝb%7ë턳‹7Ö™ìÝ´gÿüËhf´e‹@$x©zíK,nŸ°!HË®’y#ô×Ê98°,}x¥^›ÀÜ8OzÔçXIìtEè/’±×>ÕæŒ)vñk%rÐ!•ÚŠz}ä/ì¿=TSü2®ÅÁ÷ÝšA·AQ^‹Û.¸þ»U=cTõ tßÌé=Íäýq0†¹O ·²Rgåyø™B¬a•7œ)³Àê™óýŠxõÏc 0a†*ü¥V¾´ã¾7@Ìr†"djiRb‰1,;yÁ’œÉ¿dU2cpÏê>‰¥›.pm$z†ø³Fé¿>p8VvÀsì>N…Æ€©xÿK!†·vLÇá| ÷Þ´sñf5.Ï© @åÕ ]œ‘^¯OÔ&´ð•Âz…(·\ˆõüGÓiõ©QíÕ{ϧY¡´l´ï™-2ì;bžX›4¨†ÉÓgé÷'kô¸Ëçyrè#®£šÌ6LG"@M¤QùM^O ‹Òã-€Ý|4ˆ«etñöëS¡è£šþêúÕdÇžúp¤GkÀ‹ _½ïxu¹|][ÌÏD»­íØñj3VVZ ÿj³U®, þ ÏwF°¦·èå´å(u>ºe.àÖ2a:0sÅ=7&~ÔøHæ›ùÒ2‚™/2ßq8¬SãÇñdù\C?®ŽµÇú#°lu:1¼[ÅÏvŸKuu£;ñ  Áüj¯Ÿ”>E﹯á4¯»ô졉§=ÚÖ}ZÏ¥Dîc1†n­oRPˆ¼òýô Áw& :Máo6»á- r0ÚÌim<Ï/)¾Nždû§"ÔsV”Iå¶/H~bçaÅi!Á²Ç*DzªP˜§h‹X˜F)e*ûk÷1¥‚ýþ¹·ÏìÖk^†¦šV Ø`€(ÐÓcŽ$T £ôùf7¢Z2016zjೆÄK¦¸EÞ•ÇßB k14WË-NÀ³Ý|]™ã|¸7¬é5-EÀĈR&¬aWFµ3ÍLÉ:…õÄ~¯Õ åÅÄ*. `‚dkkpXû4ß=DVÜý-1U·é'ߥjÃå¨)»<ºiý'H÷šN³ŸË{«9C³b %u*jŸåH NaoýÏøh)¢VÂd8Ü¥²•ü‘ºŽÖþÜKßšÕAWиg/AGN©! LYÁlCœfŠˆ\»ÝT´ªŸ7AÜ‹âu2_Àø~D £Py©‡ˆg­]@ñÅ£Lydö [ÝÂÁË.xã¸-q›¼#˜é÷RCÆ7‚g”eü½Õ®;ùòÅ4î@•û³´‘=… ëûö§{=‘/h*²–›þ!ò¼|·JËT¨bµ§6[k¤øÓ‚T2É.~ÆËÁE,b¡ÓówFŒ™=6…A¹˜¿1Sjl\õ°%Á£‰®Z+j–¡e1ET|†ZžlÓz×åîÌq»8(½ZB €Ì¶5MÙï.ùí^ ¼ºK‹zEé»lˆ¹Æ¢'¦Ü†G9œÚk o‰m[ˆÓK[þ Ÿ,$B¿ÕÜQûº-!$š™Z=Û ”m²ÙÇ‹Kº M¦Œüt÷v¸à‰¥æ+~µ\Ê_HVt8S%lû–~Sk×O*»(Öÿr—Øÿ<­yñâ †ÂóLY-PĶxló>mÌ®§ÐÁn—i±1U /9šd°5e “º—3ÔË^a]Œé'wæYÜÜ=«|j?´žýܹŠ7eOCjôѽ“úù€OŸÍç–LÜ4„yZR¤b! #šõ†]žGâWìÀ_#Ë~’°¬iˆ¯Á(ÿ]Z÷`aó¡(˜~L(‘ì<É¡)EØîZùÆXKy¼a>B±­'Õz1-ðUø¾W‹<™ó,˜§;MôÕ£ÍOvú¥div÷z]à#"ú c¾›ž9Û‚S?ŸBi)LZè‡$hé=6Ó±öŽQÖ°(h¯¢«B˜ “zMNry…²w¹ú¡‘—ÿJÌèXæ—V@O¬ ‡3¬²%Š?¸a0Fo@gmWmt_A,•¢yô¼r©õÇ:‚çe«Ð6û(d`ÞŠmÁ<ôü..ånø¢:U?Sýã56ÁÎàå°gÍ"ObЃAo®V•0%l2ɾx€¦Hdf”g #h?âGamQËT:P¯͵"±‘RŒ¶ŠÈ‚@²±im†9ÎÚäÓ?s`L ”RžçrER¼R`K ¡g5P9 ¼±ñ `"­ ¸œýú·û`u ©HJI¢œŒû…6ûK"䤼“Å‹V€Ef¹ÜúË¿]·ÅàÆU]†IîD}6î({0$¹‘0;%(¾RÊÕó3ôÂö¶W9e^_ötêÖjû¾¢7U ž ö0nlz4Ô‡ÕàÿræŽ@žÐ}B™jBÿ\Δrùb5#3ïµ8¸j“ñ”²ïKr~ƒ·´ ½F¿ÐýCéÅ7:öus«¾/®Jç)iŒ-÷Õ“s«}I\ãfÏŠ®àIpÕ¬]ÑÔ 8ºzð בê¾9 3w»úêå…ÑÜ¿v¨k‘‘Xý¹ söƒtø¬Šàÿ{È y´å 'MFp¥E‹D´:oÅÕ¦+7ž½¬DÚÊÌò×vÑnS±õRfcµw¾[3n ¬w=HÔEXЫóî­gÒÚ‡&*x£p™“,?ÐkÜ`©ý‘5 ·ä œ§‹­:€b.¯µš3ÇWµ3€-#,ú0UL»ÀU†ç¶H…\%Ñ,§Í@-¹¼`º½HJŒô•èJ¡n!Õ§¶ìå\¹émŒsÞ×u=g34¬”€¬X±ˆ[šH‹ŠÁïk’?ÿå;[1=‰ÔÃs¶4tø¾dŽ©°ÈjûÌžŸøÞšÍ›;$Dï8'ŸØgPÂê~MÅ yº~i´ñüÅ%rbìçr¢U³‹çÑêš “1dNaQÆÉb*ž;ož>ýMšÃ¼Ø‡p˰ná“Cs¦“áÕný%$Ö×hLçYrºí7bK±ÁÀ‘»|H%Ô²'ù,1æm“»hL(墑ڋ©~Óœ¦C"ƒ?̃5IZö2ò€4ê>¢ÉߺâO½jùõ«¹ûk(ªb5ï=ñö¯†MêZõPû¸Áª+ÐÀu‰Wnîd>z¸0ÔÑ¥|Kß© ø2\]y# pT?Ë|…Ä²í¥Ø#}ƒ¬×Èú5° ‚Ãr*æK½ùŒB]•2Ö^J%nü\æÝé wˆ‡dO×g*¶jŒJÕr¡˜‰hª™Æ»G+ëà¶Í÷⻹é–S©àÿ6¿Å®?—.ÐÑ‚ZÐE 0ÿ0¥)¬`—-j"'ûÏ# Ñ‹¿ UP¾Ù$Jœ’ˆô oÆ‹,]jn.gФz ¾M¼íØõD`®FV›0ÒA.½ihÖ‚O†©Þ6ˆŸhª| xÐ4‘Bsù zw°‚tëßhÙB•¨qCr¶ë>6i TKÏŠ¼!ÚÂòŠÈ¦§“º@¾‰Adþ‹@5FSÎC®àŸK9—Ð#{QCÈ®—×Пx˶+BÔ¿Aè­¬Õ×+°›âòëùV¿¦ôñ0²Ζüh?B»>gœnØÊ(0y¢—¸œþ}ÆãìõÒºžn.3P|EK3qh‹0{y †þ^wã•®¸¨}Â~4ˆ.Ë&JŽGàêøôcÁQèz?¼DX®yp²¾Ü;'ñŽÞ¸§‡ßÄ5ÍßÂÎõ°º¬;îS z\?§>Œ‰­É8èÀR$%¦vÉX®Ö" .!ôKpmDð╹ ‘,^lŠr\ËlïøÓúoÍ)Î…‹±¥éIìˆoG{¬Ï܇‚ñ||…|1j{L¡y®Au„÷ú·5þsÜôyyücUÍÍZÀüäš1½MªÝ¸˜ ÌV£QÃK—þ€¼Dú1„vÛ^!áÙ&‰VŸ+¥iHþ›Å…¤5 ·\:PiÕfä¬íÈÊËdçbOU/KWsÄÏB~2‰æ 7×e qø‚寧 D¨ËÔ¡º¶+5Ãõ0ƒ>XPvh,…b¹‚iïÞ^õ'ˆÛ0¬|” ³n–Ò©››w|XñÏ7¶+ˆGQœ‰ò+º-žPqÉv"ëÎ ý´0fà‡<• ;ÑQ ·úÝ„ Þ—©bœbxÁ-aA ÎØŸíƒ$ ñAo&TM(6æg®Ç‰.ÛÚ/Τðix Eg^>y‡*À'ÑxªKÙwƒU'nMXcœ>tÎÍå,R«– qHçïD]ƒæî8›ÒØ=ž²@šÔšßR\#´ CÔ‘ŠW¥5G“Öx­BG›Û£X\U3\r}ª­MYBÚDñÆ„[AùÆQ/»N Ns‹×£6v"zt¹Ó]Âöt¦‚6qa;‡.˜º¼rÝ>[?=4]ƒš'|‡:÷¸,þºÓ ¤Ûù2ᄇÞ±ÏÌüeÙêö‰Íz¸+ÀÙúeaÕ fºê·U¸©G¥}Ê» C6cPþK*Ö+G$1sä ùDãùÕ}܆oö‘tFzS‰@èŒ É¾ ÐêŽZ®f™[èŠÂº.þlY—bùÈœ™|‰àÜšðçÒˆ§ì™w»71yM¬D {¸ d-³ºK 'l}E PÈsƒwP[í €X2ÑÄ@qÿ?¨UÁD?”„µK³ Ùt<âz²Ku/×, ¹=ªƒ¨vï†îþ2ó´ûÄu›£ý$…Ù‚ò»öÕEÑts¿×1L(½7úêáèÕpì!Í’9WŸ‡O‚ÍõýÒOåt;A0`D\èø/_a>­x-ÄÅ¥ €iˆ¶V¥ÙZÂ-é¿)°œYìŇÃpIÿ}u:†Ÿ—(v)ŒÏè×uqz„˜O1cŒº\ý¤Û´G÷ò­”NÎ¥W]Ç2®¶ÔÑD,ÉÀ˜ü’ ¶Â*uÞŽråÚ5 ±V1WB’™‡ÌÒ#ÓHc’Ýy?î*Ö?š‘\^I¿ßÓƒž¯s/ŠKÛò …mvíòõQ¡p&0¹ŽùqÕÏëY¬åU>âªàumð oT¿6 yèw0¹£öêî›â£:ÿqúÓŽv×C™<ÅíòP&œ–¾Ýø’L2Ì/›,ZüßHÖ¸÷¦=ÄÓ~ÚjÃáÓ[*Ÿ#MŲ3 :€³Æ:Š /èFûmÚ }¹ìNôåS_ˉšè@» _Ù(¡)õh#Že&xP2š;=êvå±{ÊBJ«…ɪ6ì¼ÚA«g?•†x©ÓGÀ9Š8a˜$,*q޳æ/ÆäÙÈ–§ÎÎÅt-¯7!Ǻè=•”[eícØK5ÀJbdñ´Ûº[^®N¡ç^©¤Éª%@Ð÷²ùüºpu×j-U—~‡Ïßôù¢z¯–éµ3Q¼˜eú5ø*]Èfr6±ñï=əͼ‡/•¹‹Ž‹E×[WÀá…YPÙwŽ&†Ü–»ìËâÇv)b©]Wþ_þ¹p Î(±!­(…xzJPæzñV¼ÁI°D¦³¨»–2|Õ•¿òßÝàkZäVߘc +~æ—DÄÔ¼m·¡kã‰LÚªF? - Ç~«‘ÔlàËœ ^`ƒÊ`NûÅ ä Nyõ´[…l%Í*(½ð L+B5|Þ³Âc;°ºÂGOóžl’WlΉ 'õÊ-ÃXÜøÏ·y{ßêæ­+LúHýL¹Á˜¼â„çâàN÷†±*HeØq>ÛF¦Ôœ¶³ö_KUŸÎNõrµæµJç'Â[ŒX $hô\™£¶ë凙•§ä\”2„Լ͵‡ÅH[ÑÃçÉ"@ÉóŠÊæÈù–½¬yWŒ¨ÐÁnéÍÕB+™Cî`ààpÖå)<‡ŸF?åjÅÎÌðZöøçeÐ ÇËöOd†]÷Ö¢OƱ¤J^V¼jš8Ä1é^Ïï8±¢VÐ ÆîТ †Ê`¦†Óéôà s’™)…­:ëÏjbR¦Ü!ÆRÓýê¸]º³{ê…ÚÛĈ߯æé€ÄEKŒüÜöÃô“º)ǰ] .o$¢:¶ñ‡¾é˜]ÚHÜ’Ääú£í áTÁ3(jv,Ü WN²…1{ ÑMÝ vÕ[59@²2‚ƉƴwŸŸï¥ ÀŒ2>0…þr“KŽ­Åäxž-^Ws”—¢ºÑ†8VRežKA :è‰ V (µeЏIüµ¢+º®¿Éºù…Ø/âë+[r¡ò+æ8ùïó )‘ÆÕnቬÇÍŽô73ó>>•½}Á÷x‡Ãr ¶F­1˜S®æiEY/†eÈ÷‚S~Å€JB™nZJ鑤,©“‰lºþ|‘`ÒШ?F¹ªR]zñ*ršZßÜßÔ‰Zsi‘M1g– txÊy¼Èn.!]Uõ¾;“9[ï„çöЂe.0ˆk²¾x–ÀT<&fàÔ,«UP›º°Y3„»Ž;GXþÖ.öû–pÑlÜ©ðe’·þv»ã?úÕ85ƒnQEÌ^FóSgÓß(wârO‚Õ`eöÞ²õ²|q1½ù—и°nstEÊÀ‡ Ó|â,èðÁæ™±H2p}eGûeöœÅ¢À_Œ²¤pëmý$ÕV´›X†wÁsÖç۪ɡÏdêô7×e6I½ÖŸ\hƒÃÌ@IÉÌdýéÿGW<ãB,û>Y(/ 4h&¢Nå¡Û*JÌ}ŒÑ~¹'%®¨ÖQ—à2töÏ$à¯å7çÅXqœ±ô1ý33•ÆÕôá ]÷I1 ûYÄ„b!wqǾUÍóô4^ëÀ2Ïéš/µŽ¸†§~²‰ ––Ø=ù’ ë…}R*¢CE‚£B§•êýdÒÆ~X°A´»ŒœÅ ›Ÿ†Zùrò£ÐÎOÏ0o¤Ôkœ߆ÕlÉ?Í›« ‚ ò¹-WÝN'uâŽÔnªýÛé;dø:X1+ã7¾ÿÑsZ±RòøH+gÚ‰9;æIŒ1ÆÍºŠ¾‘=u]Š™#UÄe»Ë°kŸçЇ@ÚÍojj*k³wož´×³sÇ3ÊÇ h17ÅîŽCg‰ïW‘–‰TŽÍ2ØúôewCkýÓ댚ìQÔ´A†¬¸J´Ú+äÀŠ»ì]ãÉ$6„Ó a†&ïP“„rN«èÜæ„'Ìf6ûù»‰ßVœˆÙEsÿJ¼;øîîúðpCš cs¦»#M*Õ¶mÙmïÓ ¹¯à¼ÏÛÎ\SŸžƒú¬tÙN©T™Ê}#<’µˆƒnæb]jÝ#Í/š/°’[½[ôÓ˜i~ zø®)?ù°F‰÷¬f6çŒ8s82mh®]©)¾áoxP˜xwU~„ýIïM9@“NF"”zý–lÔ^´ýºñÌ5T—ª¼4(oYXsȲ/¨E÷ ²aÜêH'§«CgÀïñ¨L'®Õ1Fm ˜‹ÁÀBŒ09U—h>@†+£ö¶é˜fv•T™ÿ‡¯ðHQæY]cV?[¨RñQ/ÂFŽÙ`µeo%ÏZ|È&°åQy'K~®ž1˜Ò1zô0ÿ@00ú¦(}n’>ÍRÁ‘ùç²õ›ªzßÞò¹yë=é§™½7=»Š¯ M,¢BW1®EpβÓ·kì0‹rs@p8ój¤K"¤Õ097EЗ-•ôèþ.@e©‹Z1dΊÌx’ù»pÔC¢h.4{={ydXÍËt}¨onLˆb‚ ›q ˆ_sQ SóÝw‘ï¼Á¿lcŒ´Î‘—ä¼¶EÎY=.¹¥”Óí ÈAd~8=­¾²=ð£jƒÏnw'“σ2ÙY á¤!J ÅþåBW¹D&2îñ.šTC¶^5C¯N4È6ÑgÚï5ßN±Î‰Ä¶4¾ÕU‹õ{×–§¬]·7¸í©.Ë]p\±D€Kn$Ðï}º^œßÒoCßÓ,œôxÒÜ•òX‚\­/JŠÝ¯k“ºVôSßëM°íkMJ/Çßüµ!ª>E©Å8žÚµ’xšÌ=Jn sTRLÝ?x´ë:ä?}Çl¾¨&áT€u\hD˜Ä„þÇŠ§üzSK°Z)cbÌ#«o‹ïÝ?þ˜rjºÙƒ³x.ç®j2µß¯­ŠXZÑ]~àl·š%«óh%D£–4 ¨5d/T2àóÑchúüMIxü|3쎊¡…ù4ºû[¼J&Ö?¦lSœ0­.qt3ÿ: î¶âŽõ"ŽŠàU€¶X¾¡¥€D{z4;ê(¹qÄѵÙríG¬]{˜vR~žùc¨j’­ÚÁ¸xÁôës+¯ ’q£é9.J|¶?à<ž°mçîô]º8·X7·Â‚ª ÁÒÌ!žÝ•¬0ÍWúFéãwÀæ7)|WÈ 6[]ºÊ¢²Y|±•ÒLé6„èÕg›×ͼ‚x…ù o5¹´Ë;$ÅÜP>>¥P¬’öyWÐ|#'õ½‘áÔP‰ýÍŠ YË5SâêĄأòF%»nxK4U78+Z²K§¾YqšÄä´±P”oÏ3*R5÷.,Çk•þÀðë!ÁN¿Ìé#5yÞºé~Qã]]½Eu6ORé^ G>+áË óéðï!¶®v ~ûDm:™½Ê€w»³|Ê4dcH£MpŸHO’ÿj¾é‡“¡?à õ†3¢wûQ˜¸n/Ç›Q%„,I·ƒž •š€¨›HȆp‰» Ó0o-¥Ï©]àlÑÝòbUßvP—¶¡Þ­LÄŒ&•|Ÿ'®›Í£h0ü\X:[É×m¿¾DkB%.‘€,RÆV*„»pzu0Ò‚AÀ‡Cçù)Ò禂ºS‘‡*…—ždGЖ”xuº¡Èm޶6Ðú“i6~_'fí;,¦ÒalSd•òYðK#P@ˆ÷Ì-x¢YíVég P䪩 \ÛË~=fÊÖɨÅî± ßxÈÐP”Þí¹ÚP=ô‰VWõ9Û动{Óñ˜ÏfAb†øl„w‡o¥?ð¥óTµŒ…Úé|º¾^×ëG+Qû®uv†_ cB k4&̘ç;[Ó—s‚¤½a* Ñ-…änøº)ÜÈG¿¶KQÞ¤Ù#UlðJæýy•¦V«–DzdXRŸ«¹1bÑ“½/EÛEê><èvñsXýw+‰õÍk8²•äÞcݱ=Ëçýúx€4­"R£À… ºÒÊD'gªö97¢û¤6ŽQ€IéÅ©˜Ò`à>›p!@Ú'^0M;cø4,|¨§žùé·A-h¡¯2‚²î]俬I)‘h^ÖJ#ØÜQ!Ó*Ôñ×”gP§Ë?Oôܨ${ðÒñ—w^éXìP sýfºõ–¤Ž jRP™(y—ô¡‹S P†£7i»Ü]÷¼w´þ+ÖÒ@×> " G$Bk=êž÷jÛ:pìl¥2Àwgm«üTíªO-Êÿ ‰V‚bñR§UʈöÌ«ãŠn(y-˜Êçššåo}ìì¤bIÁñöÅSd¹µÃÑ;A‰ÿ{˜fR¬äLdõø`†“„<:¿½,åçFw”˜tã ›¤Ú•«boÉ©èÐßu´öSÑOÃÿa£»•Žëú”ÊŠÞœè*r±IÚÓqt@¨ÄØ|šèÉÓGHÃ¥ êvàQ´?ŸšœEÂ×oÔ£~°7ù8ÇäN[€ )‘­c] Nnð÷mÃz„¬ù˜ülü‰³GÝ1ÔUÂa[+«z¨Î€ëgÖ0U¿gû„vúEPq–}°;ȵbú õ€D ıQΛdÐ%õL½Ù餘ÐÊîGJŠÄŸC;(Î"«÷o¦[8²tÍ-6Ö|±Ä—\b×Eœõ—9Šo‰n¶‘Ë !…m&i—šé8W3û‘E¾”'Ä‘›2CŽÂ¾·ÊÉA À~˜¾dúú7ë–É…ôH63Šø Cd*ëŠmWyŒ>„+Ä4­ù‚ÁÉã—cŽ£ä ‰óÓ.NMgb™Á+»èzô‡ÓHdйT1çŒiú_p1 ´›gjkŸ[fÏséšSÙŽMÌË#ÿ'íâ¹~ylHa2ïœ"aú‚Ålö­ËDVű“ô¾C.’ÜÇÝ®° TañOù¾†jYè´°zò·ùà&´k4kß,Ö ¯Fj‡bö ax´ð9Œ­zd_¹ÂF±iœµ¸Ñ±ŸËZk슳2½<Û WÞþ~öºÀxq3SÜàÆ 2†s­Dÿ[tТò:ß«[IðXþ‹ÙZŒÙZMhÂê<¨}ºör™LiF@ðù3BMpkÈINXf‡…=–Pcß÷äʶƗ2XîÊJIÏBÖ?FË/Åô ·™u7hÓr²d°Ù°î8ßæ"c™ “€÷qu` ÅGRnh¨ôèi è¤ÛÐÌ&>šµË°q]BOŒ¤²p%0Œ·Ll¦cõuYõ¨±¾ÌgÎpñwqòIزR _mÙ¦ÌØŽlÝEû VLjhȆ£xÖ)2æ?‚ r©N?åz꾨˜²€ÈÌ/4 Äí›ÇR«ûy8Hí*o‹6ÿ\©-ñ—j[“Z„º–oZTÜ{¿¼œ~A ; ëz”ÑEºù¿ùXÉdi;o#÷œ÷ã+šë.«‡ØÇ-ä^±] UÊ=æ¡w-䘀ªù5blnäJ²^aé0y³jA5ÃVݺGÕÐàybEaã lÿx …7Œ — #×à·¥g¾W{Í!£:ìÛ¯ãö[%ÿ3‡ÜöG\ƒÌ<ÖgõCÿjˆµ{¡0,U[™#âf‰Côiìl‘Ï=O1”’I ò62Fk¥(¯öµÂ$^=Ä(€íÈOã2Â`ɶ1QÍQ“Xè¸ÿekN*œß¼OûØ\“œ(µT' ÈÀ¼=[¸j*CÐÉkkzØ·D_„EìÍ$$q˜ ð»/²:‚ÉÂìö‰vëA}xMfEB¶ú^þŠ;óã&§ô6Õ¼H7•ç£;ÛnbR5·èhÿ[Ý=•}%—ÞÍ‘p÷¹lAZ²gpfÕÚ¨&êºC3ÉÉ«n]AØáâ†[Ö~QèŽÙP"‘ú5B×á·|uµëGÀb@t%ý oÏd½…L®¦¡q¿%á¤ÆŸó)O“‚¿¼¨b_Ã$>#tUˆP«GmQqÖSpÃ]DFaÐÚ,Ô&4´ÚŠ‘™ôë#g“;™¯Á®§+ä­i^?Áb<Œ¤³GÉ)rµåÛ>]Ò6y\ Q,È_b2F—cΫ¨å¸ª0‘ °óÙ·_¥EË|[|¸…ø ¹ŸWQC£©Ï>æó.òÁº×[?ÿSÙrWÃ|ê’×}Ó8Rá½p“aîï.!áëÎCO´žšnæm5­ªÉ~»¶ ÂÖ_ÆÜf¼9l<ŒÖwX›Ì2/\ÕØw×µ„«Ç~÷ð“œikëå:°‹5tƒ¤÷èwÉr%D7|±ÒÃÅ…¤e]Z»kÍ:&|lþëðŸôçÍsÍèé|d"ëh»æTžµ"å•öÀ%–ý!(IÁ?Áh¨ çIc&/; ²‘®fÞë$­¶ðm«’œÛqPW# ›KR‡y:ž˜Î‹i|JƒN€8Q€€Uë7â2-f‡s¸ùUt|B¾ó¤0è|ØÄ&ýn* ‹:xÛæ¢Ïô&p·<4#çž9bxñ–}*ùR—ròlá‡2Yÿ-Se„å· –‚s¨#*J|ˆV±¼‚§3RngŸÐmžöM(.•1ʹ…c׉ÏÔ¢@Ôé™Ëãàk?{¥MhK¦„â5·X1Þæ$—%ÔýGÌUx/ å)Ǧ ýeÉÄEþß`Ô"qŽ÷EI’ù°81TË•?Zê¾¥ýŸ¡]ü=öÐÇЀ÷Ž8RØD‡tÚ@ý%ùdöuY]p”aq….äÒd/t¢¶˜»)fgP§tõ«ªÔžJ>’*|¾žR¶l¸šó`ìÈÍ0´8ª9ê—e/.ö!»0EåçfÎO«ûˆI9ÉM“hk~…^ù·×äÒ—}åDùˆa—!ªÞÿ¯]ž” %.M–á¤mµC™ Æ<ˆkËôlºoÀ ó¥ô'W¬o*q¯i»½Z—®DóCv´ˆ@¶I4ínÍž´ŒN½Ù‡VR%Ã-Åä"üN¾2¼'b”¤Ö„CŽÜ5¨‹  È®§°ø:‹yÛ/¯lNHwþócfïæ1è§ã$„C~¡|\Ÿk!°¸¬´MÛ”ÇØ|G£7¯š®|ˆ_CßUv÷Þ4VJt°xõupE^≵ÉòOy,á^¹Ûd´Â+Â'È¥òl­\¸K"^0,_Îg)$Êvé-´‚:ç;,T©I÷ò«úž VÕ’[Ègõó·W©™ó,lÈü;v„L Ká¥M¹;*°xŠRТ¿ø–H~²ŸŠ¦n›$hW.à‰ ¯¿’ Ïú6žlšµ.G$D3Iã•[8:k€s y,eq”Љ $™ÌVǹ´š~.?± ^>‘âÖœžÅ¬ÞX# âøÊ$k!8œ^ÑöïÎ\9—lÙt:ÛÙEŽ­ì¿J‘U“Y;>ÃÎ#6Í 6¦4#õÚžð`ò„Zò/K¦¸cõ™gÏ?Õ€œ!éµ-¡Ô¼‘Lý‡¢ÙðLvHÊ·K‡j¢dše‚Ün B#à  À{ ÇvÎ'”¶ _qÚÞ§ ¶0—­5ÂE]͘Õi(Y›_O ƒ&“šÅͨm\sÏ/4µ·†FZfÉ ß|“~ªt?±D¦LÝÛäðîhjË×ù'rsà(ýqŠê]ãÜým¿:`C¢PÞl“ÐÕ[TE–×.Þ·Ef-:⢸‹;ŠS"µ^4Yz͸L Eêæµœ§:Éó‡üÁ%ž Ю[twY ‚¯:9ç¾ß4­1¿€J+™Dº=ضªd ¤LÒ $.ã×¥¥$ 5˰áQQœüµc¨¢>œ:h ±DÚÜeÝ‹Oþ› š„£>ÀWÜ``òu®ƒ€KAˆîðk·õ7üWåyáà/%ãamH€ÉḚ̈þäÞ,š˜Æp9ââïØµiÔž\O*í0‡«poÀöÆ‘Åzóv2MÌwr¤—ª&H‰G9 {¨§ûdìzfá16s¾U¸´n@ŽÀ”‘£ÐSÒÿebM±glXZÞdt7}‡Ï|DÈ(Iúnzós©Ðx4‰°a‡©`ämƒãÕŸ'FÓ>Ðs ·vM ‹ÊNëæ lvfeXÊ~`¿PWX0ÎïÙh,ZœkÓòñ“²ûÞ ù}^"¬ …¸^ù^€Æ8¤¯ n½4ŒÒLmžc1 ëÆ¬ŸóŠ¡w/Dž'$TÖØ¿Ñž\)ù(„ãÖÛ9ˆNÆCFD÷ö#¶L4ÜÇ‹‘Þ÷•[Wš2€2&ç9ü³ô`„š—CA÷Ý{ò\™3U•é^¢ë3bw,æCÈÖà¬29 ¾ßpbeÄ»®€}*$SÿRÀdæ×ïÁŒÔ´-6èÊ1ô’ÖÅíÃw¾Þ;'Á)Ùk'ÌÈZÔšâ=UŽP½q¦nK™$ÝÄV(VÂeÝà‡[úU•º9ëøŸîÌÖRÉGdÙ:8bï ÿU•2)å=MDÍn7¼vÖ|;—JþF’Hwài):±ÇŒ°š\ÚÇÓ ÿ «Þe{ìÐìÁøÀùºÉOþÞ%¶/Î.BóQ+TÔÝ¿£NËMÈ"Gëßôm@°OkäFÜÑ8]¾Ö¥¸À8Z¬NpC-ÈtÅuØí¦ÿG‰3aÀþ«‰q+¡.Ÿq“ V§²í¨ÏX›s‘Bq_¿ª8à #Öìj½A]Ólõ@¢Æ7•Äñ¸ëAÄìÀß'Çoø€Rê—oÁŸÇoäò`P#¤ÒàgýMGX Ûµ¤GZ5Âãl;ÿ “Q‡Vïåy¿gË©ø¦ 0¬ èʲ¦3Aà4ÙgÌ ~ ^îŠ ± Ö“>ÇQX.îÙD ähW™Éè<˜—뙬TÑOÙ‹ãì}ÊÇÓ%¬²ŒZ?,Õo@8Åíå`OýŽð<}‡çIúÉW¹0n:.À˜žv>Â&,‹ôq:AðúŠò5?ÿ‘‘|›µ Èê,Þ½Ô­A‘þÚÞ· ëT6yJ{¿!ùÐ||R1NZÊÕ÷‡©â{G}dÃG‘ä¹'tó„„“e}]0…‘çj{…Sˆ‰£Ut¡üWë%³ ͨҽ—&kw*L~^ÄìašÛ³a&¶AÑÊG¬ù;Ì)wd"ç–H9öb¢Û;äÜLËAWîsQ>gìCôD{ßÚ ®Úæ‰;à»nBÅƾcMg„#ìeÉ’ø7kBÛàvW« îî$ó>lûô©´ØÅÒ™µì¤( † ¶Ê‚: ,|«#ärƒ2d`®u䎦ErýTLy±qsWa€¼Rj8Ó1;&¶âº£sÛ”ô ¬¼Y2?“¢-‹„Kºhæáù#$€ƒbÿrþ%Ü éXåØ]ôUS<ÇþPÝaKÑðæª>,’4r Òz®o¿v«ËÇZÀá~õ üÔ Ð<Û¡•vÌØ^¤ìÞO*K™¶¬):ägLlnåg£J­'Ôöǩϭ»T^$|ò5ç+œÕˆJZvˆ"”T!á&¦¤2ŒFã‹Tõ g¢G‘n™»ðÖýä–iØç¡Å¤½1M±çó­\öGŠYHÖÁxÜäÊÖm,N›;sÉÝ{q'ÂÝZ°"Ê‹„Àb.«KžØõÍ:·j”~hÚ1 )-¤/×£·¶MÊ8˜ÜPÔ;'n;ã]Èڇ§‹NMQc“ú— n~ç!2¯™²û5ë¨Kç27¾õZN÷9Ô­úTÂ乤_…ZÁ|t‰†J$‹«&R‡ß$) WìŸï<Ê…-“z“öðýnÜôë)nLªáämöŒP\ PàÏÝH~Ä $KáÊ£­šJ6†gñ³4Ô‡¤­Ø!µ#ެ® NPi)Òå¼ZÄ9G5¾ÒLö×W.Q{4¬èeN1ü‡[i£®‡OœäÃÂ"wyDå¥2»·æª{¸&Ró7êÌCÆñ¢>B>´XŸIì4=ErÍ&4CÔŒ÷ª0ˆ¯S¿‹¬ÕSDÅ:$W¾­šÌªæ >!Ðl«UAŸ¾^ÎG‡}>RŠ” ‡ãüD ºøø™îöΚÑÔo©FžNRÿ[M™;NAÓUúpKˆG?^–,âoS“hÑgÓ/ÃÅd/º }c/÷àO†Â)Ê^.’0Ä4 Ä–¼¨Ò…í^ò¼<û×ûI¡[ŸÕO²¬ËGà… ×óšÝ›É'úàœÌ‚d‰(BXu“Œsõ2ÚÁ!ôÇB:ÿ$FòIÏ:ãÞÄx3$°;%9Äp9¦É`¥þ»â@k(sÒh˜Ä§öXÌ#‹ò g¶QŽ÷¬£ˆ -Vwæ ¾xëdî‚Vš£²8/qñMí®Èf,€‡EîýËMZÇ}ýOijÿ.ôrÃ,]Ši*Ùx8¾¤žNmÛ*ϲRÌäþýŸè2‹ÝŠ6›‡É’“fœ¤g¶Ïûœ’¢óo6!9dBš‘*–Ây8!-fà]‰¨ ¡Y«š‚—[œX4‘sñGuV–eÛ'ôƒÂˆ0ðr¾0÷òƒùœ»NõþªHaŽ`j}xq¿’ÿ'ÐõpèáÑ%ûl)ž3$†t‰÷ÒàÝk!P@A`™eÏÅlyï™ísë„~vR£‹çÂìšå¨gð6ÁÊç|]1ö±ã‚óv!²¬ã¡ì2½’Iøm^ÔlǧÓ:BÄ蜗…E I]Ü+!áOÜi¬›G¾5/»¥Aò]c’ö‘æÓ·à¾ø¸çÅëÕ®X×"ƒ~Yb£j,ø"Tµ9²…G.t—§ø“þ<ÆÙÙ˜üjøVCÑëæêFvE“ºÇ¿çÔŽLð,¿QÝÈ0¦£ÛÉijœ»y_ƒ~Ô4!\¤GTmU.½GÛ0X>üU‘ÞçÚ*¤¥+ {%Šg^ûšo÷EGS<c wÄ9Fw_Ø—¸ËíúN£Æ3ŠU>ßeÎû¢ŒèvZ BL;cÙNb»›¸y3N@Ò€¡¦âïË‚‰E#Rõ ÔƒÎ×¶ä$..O\èè T¯?Ù‰>éDh,• uLô~$kŒ¶U©®É#ÿ ЖçIiív5aƒïâgsý¦(4°×ÖuP¦²›à¨dø[‘¢%³ {u/­ Ñ –07ä•,Ì· uü÷+¨fºÕaírÑ‘¯RÖ A! yg„[`ä}o·‘ÙòÒ-ú8Ÿ@÷Š}à-BQ Kä~lfÔiVu;«Ô}uHwÅë¶” ^Eð|0„ßAúÌÞf~‹s1„S@aýçß+½‰€ò»è { %XnJ0³4ÙB-½t»ñèl4ØŸo*´ü£×!·äo^.Ìó >àæiµ·í>`:¼-â&^?È¥öá®®†ƒ Bݼ/:P®/\¬Å®Ó&÷wD?'w§Ì~â#Y"ŠÄ9|"€Ð – }âÌ åéq¥S8£^æ»bµ´Â9¢´ƒ÷Ë.èdߨArÅ#›&Ó¦ãÚßÃRɧԙ…ª4w¶n^p˜µ±ËÃßßÄb#AY†{c»¸²”»™spêîk2§ÕÆQu`=‡KŸ*"aXSXð¡LiUÊÄåã+`ý;ͶJûx4Œõ˜QÎì­…œd‚• †BµÇ¡0äò›á ~ÛÅ,èÙl9â‰ùe¿6…ëôÆ¡î%"¶‚¦ßšÉM Ì×ü/ÈÏÎÐTs¾‚5“Gø¤¨»¤â5FH=÷º'“Fjp¥Æ¦Ë‹Ýî™7^·Kž²|ñ“Àö£týXÑ)¸ú™±º.ˆq¾]7ˆ-ÃrP†V¨Ìiø¨[ú0rã¹Î»ž®C€‰Êkˆpê,šH¬5@>OÆ ÿ[lf|Ü!è™ó²ݵf´VmËÁsÑÊ?öQGAu›mîŠ'ü˜XÀòz×xÆWˆ‰ºz$:À¨î´V3þÌq‰øäº†«´j8kGó·çãü¥»’„üÜXªz-_ûÊSOY}SÚèñÑ•”ï=P®f8ƒ–31kbKP¶ƒï2…{ Z1Âòbb`uœ0°–¬××ʤ_©^žb{V¤›Í`¤ÆÃáÊ3â±î7½ÏgdwŒÈ5ç·¾@¦¯°ØþÐjmØÒk„¦Œa‰_„öÜt—\Oc¶vŸé¼Û¦$æs…‡ØPò…“Ѷ»áøˆÀÞc“%—U4B“Е‹þ:»YÏm– ½Óº.dž<+H.öC? iNƒÃ)Dò¨E*ös0U çöjwBkRNQƒ5/â@Ø\BÿZÅ @êÜoiÒ€(V4]u+Óg h¾’ 8/õsÒš$x†E gœ–9- â7 žôÓc5ww¶ÅjxbˆiÙT@pµ¬&Àã4pÅÎZ¡O3eIhX7®÷nA›XØ56YÖèÁµKˆ¼¤Ÿ®jµ²Ts#|{‹ÊQê¦Ð£Å?FUMOúì-'F/ÒÜ€Õò^«6›úÙÔÅ눾‚3ÜP:(êf|"|³ö- 3[J/ÄtÈ{ܬNTŽd_3·ž¯ñÐP=¤K^½K+q?·Q¹Ü9‹[Ýœóù ƒW¬šx0 aL2pVbï-¦bÓÅÛ*‚sFëS…ýÕí+ÏêÔkR|¸bßüÚD Œÿ=é@Å(Ë7‘B00ª€»„c<˯œ·»‚9næ-€ú‹6å•ðC0?ë©ñ¨T;¦-kéÉm¬#âa£Œü+nŒ9y­‹i@÷Û‚…Á»·r®¾;»¹XÇhóÈ«ã‚"#y9XUš1}J ”¸‘B6¥ þõa ‰€¤a îGÔ áp¬¼ð âþ/X]¤ÇoÿG`Ç>Óÿ>*4ŸÞLh“&¡ Ü “6\•‘£iŽaä±H^<”Ð[ ©³_#ÓšUŽsžùûÈqZyTíœdXÞèÏívŒP¨59ÆCâ·{Óq”lÔ^Ç_¯p’tù„ mÇ/mÉñÉF—Ô—Ó”¾î<àPeåµ £æ2:Z ® ;^p÷.6%ZªÈ,5C~áx;+^Ž\IÐ%~*a¥‹ùëoÕõ7Þ[¶LõeèVæ8Ž¦É¯Íƒ6¬4CßSVÔt“ßeþ!ÒR¨H+ÁT‚hÛ‚%ã²Çë0ŭ¤ÐÌwÉ‹á´gŽG ¨Ð±Æ÷ú‡w$¬‰xkÞ;¿C§ÿXò]‚`hFYiŒa™4-׉úË¿­¨|XQæQ¼ ¦Êð!`-.♵‰ Uþìãʤë&˜äËþŠ46—"‚9®&[ Î ¦²Ùû™¸ù9†8¥XâW±ó’b 3Éä ãô‹vöHL>ÿ(Æ£r›´Ë½Æ¿Æ«5æÝ Ô¹1¾A«V€û”`•‘ÜlæÐDÉÙao´ž€“‹¼Ý t¾IX²§u°€ýlŽPÍç.QŸ¯Ýx†i-.²÷ñIb'E–ÖÍxSoï–¢Ÿ7öÃÙf­ƒ Ÿ2{hž/åÇq¿â!Ùeý—‹ ÑÏwðXa™ à¾ù°õjƒßÓÅU{w°!“Ä©D“³«T§¨WEÆ.(¬…i.x˜¥ˆ!#X§®çXDO÷rä~¹½ãжìô<­Ç¦—är kdô¯B—˜×h˜NœóJ°Øÿ!+š¡=rÑœ#úRYµIRøÖÀ§áºÙ‘ ³áØÞl €Íöeƒý;dì1èW?½ÿp!,ÂÃØzDX Átå–òM=Z¡DÆ‹ýÂ_õ”ÐrËe´ê#%Ѧ܆ÝÁGŽ-Kºß0=ZÖ^#©±‚| ž,=$ì2߆i¸r¥×3ò °ëå»Qµþ¤4ä¨5y$åÅãìó?®'ºBè›DxAúñ+»Ì‰Ø¨zùªŸ„]š¡öóËMCP*€¸äi¨ìi…^ìÂ)ê@Yqò!3C‘³!’‡3‚xší¤ÔƒSP9K ™\ÉIà(h´&á"EÍ~mçïsáÖÚ3Èl'hÿ\ëÕÎ?+¼^•©¥í Êßµf7Å >AßÇË 5h„æœó¥uXýcDöœ .0/\ADå6›L ´Ïÿ£lõÈ¥•th–V±ÖK ÚèèæÂ9Šu LO¾™o^j ñi‹„"ÛÚŵhCƒ‰!²¿Æ¡> .c‘KSW´*©-•a”u°W¥žX!©'§¾è[Äž¾Æ ht¼Ýr[6^h<*0diüìyŒnžk` ÷¯Q¢ŽbÆÜ…Ï96GmÛëÈ_÷†==¶¦°Mæ@¡çoØù–]®¼¬fb‘¬Ïö¿ÁGX¨ec’ᎭdÖ ¿¨*€{ÕïÓÐ1âBl.¤=Y‘?SVu¢ZìL>¥gß#¥—4žöÝa|sÇ48ª©oOÝ€ñ´‘9Ï‹ðƒÐçXÉX›UìSr¦#¤òF\1Z;z„)Š«ð¸ù¥© Iá·Ïa–™·j¹¨¯Q[h²†p F —Høï4¦=±†3صçÆ‰œŽDuV’ôÚ£PäMi²Ë =ŠQ’¢Å˜˜ã`·yT+?œ{p'úO¨m82â?Pƒr%bC^v&“¤6ƒqƒŒ(•¹®¯w½÷»¶]>¼¡AÌSºÑô…ÞìÐh§e~pƒ¢P§;b™C…\Yž›ü"Ùüñ!Y>á/£÷ÝSyئvwW-Û?ÂL:¹œ–=ÍqAEÐìÿ'í°ƒ‚— |)4Ý1,å¦*#"‹ÄFÓÒñ“ºÀ#ÁWbûÛöÛ°4ôúÖ †°*22êºÛ{ÆÐ;,™× .D9»ËS%³]<¯Î²û¬EH“³”}´Lýˆ}†sÆèÿÈŒCõ¤"ïà’Hó‰ôZ'«†ÃÛÐݵ ,<ÏÇ™C’i%2ng'*.š8/”ïcÞ[_ÉB(¨å(ªs†çä°Ý³‰K¿9İÒÛžNCú˜Cp>>»Ÿ,º!Ä"¢Øêl·š¯óKÄ©8Ö½^Õc|gdËB ,»t¤í–n²®ÓñR¿Ús“‘fÝÑÙü—0¥”*Œ]ˆ òØ´dñß Ù©±&x“€Aç— €G­ßb¶9mEQlœÇ=¨tý{ô[ ú‚Âóé<#…UxŒ¤¾×ñoš¸¥U§Î{L³·…ÝJ­, ïθ|Û7È7ë¤èø+†#ÓuÆäànDjï"Œ¹éyÈ”‚Þýh¢ 'ºN½D Ô\A»*¬Ç‘õýe/|"# 2Ž@èÍh›DTºÅ­,66Õž] +H;e!«bQE$÷s–53 ¿9ž¯–9`$¸ñµÿN:íl»Þ§è59ºÙð‘Þ'€$o;ŽÑ­Èj«8c$Þ™Éå!©?ßü¹–¦ˆäY-^KÐ’'LÁÒÁÞ¼»àcs¥ÄW:+_Ë¡UÖ/‘ön ›z€Ê]¬¿–rHKÒ,Cm< ³,ò%_“ćWš^æÔS0«Þ¬¬É…ÔuŠóÐÙŸÍ8»u;óZzŦ©¿ñg<Æ{‰ ÅÌ`ÙÃ&í¸ad7unR6—¢sŸçÆ ÏŒÕ¯-a‰«0Þ·eˆòøà~µ‹Mµ]¼¤š”eÕw=H¹¢¦ÂD„,mô^‰1¨žZ ƒ`YV<ïGáD…Ê"fåý´’y….:Q±UÌö±f%QŽ_T dû%)ÓtèZ·î!MÚp¢ÔÉʼn€1Ûauöª‰Z=¸611âA'î™ÔælXý—ò‚E&Žö•º¢’Á%µ6Žrãx´ŠqŒ’LñN˜¶B~¶™¦ Nd&=´@2´Ê‹%‰#ô% @¿BQ†!•6}ôìqÇFU'¤šc•!»Oãþ:¨Xxÿ6¡æÖU’>»þ–6:4¼Åþ^b®Sw|§WôÌîà›„4 æ`ïvñBŒ6ð?âxˆs‹­'U®2ŽíVûj¾ZS 5èŽ>MLa[¿@/ñ*)ã÷DX¸’¡(éìͨ$œS­-~3nb_'Ú±Îäù[nAXŽWÈÔ‰µ¿?ãañÝyšAöœГ¹8ÛBÚºÀ³é¸_¿?qÀKž°6o¾èì’¨až`†“YÞaU×õ6yZЄ—Ž·m5 ¨j5Z£ Vy¯èêÞô&_ §À6PcŪ°!Vð½éIB–µó‰ÌRÙYû ,(+$LˆÔ²£´,Èl½\ ê% ‰T$ž¡…¸Gyý›ie”1 Æþç¹åKѨÈÇ2p®Þ0p‰}ÅÃÅ2,^dì©uâ—¢Ö$ã´Ð9±nÂèi‚2ì¾ÌÍùB×± ]…õ擾…¢gOlkPÄØ>ɰ ² å¨m?Þ¸…Üg¿™`4Õï¥Ö±è# Ð‰ªP#ˆÝ# Ï<5§R_¶©UK¨a*±š¥hno貞z#„@`6cH-õ.2ÈŒul°eŸèÎ*š€ËÞ9|ºé²P.Ð; ¬ J2^SÀR<õãïs´\µœ£pÆ$sÌØCkÉë7V“mGoØÃè_%”‹`ÞFnã+˜ŸØ°F–aá?¸Øí¾£ŽÎØÐ·yÖÁ†ð³wÀ8Nj‘ ’¸E²ñé?’‚ðžš…çYÙÀާ ¥KK µ¹sgá`ªÁ'§§¥Þ„{‡¦H\Û…_i’r!db nÎúº}Z¹âߨïo³¿d¸.L}Uä[ÓŒãæ5UnΡ°-ZžuT3"p=¸öñÓ¢Pú‡ýTšó×(*cî .tBrhõ181 º9!2•8§ë£GÜsÆUiׯzzñtÛC/݇'7Êõìɱýzë¶«Ì[Ÿhªö{îp10É‚jø2pXÃ\¤bËVGzž‹aLí§}1®cX7ðœ·Â‡5ÀüZ’Ëxi Ï;]Ei»©ð^p”ð ½ehöX¼ç–ñÒü1ô.—WñWúñ9±¹¸V¼u¹~(5Êׯ ‹FÔßwï<™Â«¸Gxd«©BØu²ë…‘a þ¾Ví7“Òx 5ù Ðü‹Î°$ßñùÊsP‹½7ú¯èÚ¼}<Än¥dL»b~&‰®ë8qÓSZÏò ú’{Õ;l‡Vô‰ƒ±dÃÐý-ˆé·w8Átøqôëoƒ‚«Õ,vÑõ³Vè ò„õ·Žø±ÒúqWØîØ¢#Q"ÜÑJÉ~uLÙZ ˾¼¶0X#z$•¾w¹°û$‡*yųOûXü\û†¿4çûë(Ù|` ü×)–¯nL#›Ö‰ÀˆîJøãi”ÛÆæÄÍ€kjœ'>_t»BÍÍ…'‰Aü­'—ÎKþýÏÍ*19dRN•Ϲ –É ”(,#FœÏKƒº¾ûªÐÔ¥å#±m>= OC=‹lAañ ÄKÑX…©¾g'Ødrß}ˆÙ>™6Rg…¬é #tŠ:böŒ½1¡p§œR( ÆqVÏÝ.Jã¤ów¾]¹‹Û#3Þøœue'¤3Õ¶Ï”ÔlKWR¢Ï':פ¸N„ˤʩßÂÇ™€ü8í QWÿòmÔ" ñ5‡ÙIS»>8=ýód¥E£(¨Z+)\^Ø:18ÂF37{#*—Ì•ï¡Åú8;‘¼Þ9ŠC¨,$.ClxÁš« µ§ùõAôZyË}¢°ükÖxúé3Ÿý±Ç™xúÆà×QmÐv%|’àÊb£2 éóûöKQ^¼ O¶w— LÜ• Vùõ#üÑŠ?Ù yçL§E“Ü‹!Ê@Â3¼Ìl–Ê9ååãMÉzÑèV *UP“åÙi¡jA¾€òG 3‚«í1¢²ÎæÇ FüL7>)ÌÞÄ›«|y^ØkN¸‘5| L (‘’™èÙð„P;ÛnÛ|ëAwmú0,ì4”A—BMô^©öê(wPV¼G4ãeÀÛî·½Ü&gBaµÐä SsEŠe®CB}àVyjŒ4Wßæ¢'Ô-“Ü£ˆ6‹œÎDöXéüÚõV0 Œ L“Ó"O禙9Š=²í|Å+b3Z£3,%ÌP\5¥¼€ð¤FÇqfÏ/  á³þÙêp¬-—5z•U‚hI<09Ödo´¿ÝTz-ÛæÓ¥ .ЩUÓõ!R8gR˜´ôF_@r‘m \I7Ž»YÏúü)÷|bŒûqô{ŒÀ=\xYÇüf÷ë(ž£L‡ic®\žG¤Îe#òºé+âEgP +`ˆéVN'àn¤Ð‰_`½î÷NÜ ãž ˜÷¹¶#ÿj›l ÷?ŒàÈxdÈþ­H1YMØ[†ádÝ*›¥Òa¢ð‘E[p¸Ï]š,Žc«nF¢´d…Œ¸+VØ$õ“•ÓNÈ'ÖbÀB†ô´¯VÚSá en¥zso`íéíÕGLœÞž#ÂfؾtLùZ6ÄDÙåeB[æïü *ªÒ>d†ÔþÉŸá8a2Vw¹ …|ÏPlz[áÌg}~¿¦VÌmWÛÁˆÒEÆsÿA›NF]£ÚØû8#µ¥ ÿ?H9Ÿà©mÕâÔ^Ž­‹„5÷Û¥}…n­{Cõo!ücÕ›Ý ôÉŽ•ˆÇ˜ñi•½~ºt±Â'˜_ ìýa =GAß¾¬Ö¼]•ˆ¬ÆåïY6®ô £½‡\ †â¿n˪´Vž Ÿ…tw,–ä#züg ;µØ”' ¯ÙK˜yÙ’âj¶!†ÈÌ»OsÍB:éô TíUHãÖ….H %,ˆê×2ÿ‚¯¬8f¯}Æ6gÍ$ÜóÈxÌÇ t޽°ùÆ¡äZJ>£+S”ŽÉeáä«D™‡å¤P>Úéµ§©¼h¥§xˆN¹Õ;8‡ÏŸðæc[‡êø JDÚæœ•”§&ŽösÞ&üúx7?§Óm‘{/pTPjq¨_/½†uýH޼ÀÓ-J6±çMîкͣ×å¼gJ~úœ]ŸËš R|P¹!r9³ò~+òRUI(ƧÿE#¹"L9ÙovvH©sA1Ë—JÿD}FñQNŽŒš<*¾Ó…^¦‰ñÌ)"?;P5ŽÞ¥]üƒhøÐ$TÎ{²³„'L1B’5ÉÌÈåúâøD¸™}Ðá«àJ§‹,ÎÅm­•údU† Æ dñªšçîhzãrÀ•f§Çù´–Á%ø÷Eƒ¨:B¢Ë¢øfÛ™Kt»3"¨tß 1 ž’ò½{ è‰8àš’Ø@l„‘Qiø“Ï%W¿Bì„çš‘vo+ò %<ß›¯é-›…\aèÚñŒ,ig áTK͉¹TÅ¡ScÜÉ*«Å>Ê.DF]Dà‰`ù(0,|*ún·ÜhÄ‚ f?Qïë…õûZ”+×ñˆ(öÿ/t»Éh ZËá;gÊ¡¿êjU®ƒñ”ŽÊ+Ó…–mÉÇH§b\e RšvÀÔÆ½$™–IžºYÇÚöWOÏeüIØ,ÊÏxBT"¹‰™iýΓƒ¬7;ÑX—©Í·@¹zÔ\_áè×Å2žÎv:3>ÒEébn[/ygeÚü—p)\¡„¨èÝDÇÆòB'ÖŸ¹GG$N§ÏD{)Íøg7ó{·™%0ooU”8a¥ÇÓö~ÀÙ¥=2YQ–­ïPnuQÂÉ6šr{9ÙÈö£-¤!9\ãçÞ‚®¦úÒ0 ›ñÍw+=¡Tßá`™ ò׎‰¯h”ÀDâ XÁý5#_®“Â^ Z˜dF¦¶ÑÄîª\N§¿ŸBÎRVA5ðDlåamO¢ä.«JÛ‹2×8¸Kùö‰óˆøFrÒ£tÙ·¼÷Ô_VˆEÔ¡ì¸óîl A«­•Ÿ&³4¢¨í‘(2².¨óƒBÇ)¤¿²õÏk!ÏcᤠšÈ!þ$nO§fSp6þæ³×k¹Þ¿ª‚øCÞÎ%Rx!X’ ˜òæâP‡Éo¨›_Z8ߊ¨#?äàCÑ;LVü%æ¡ÊÇõ&X8ºßíõàÀcþˆªÚvbøp«WO²×k©‚¹#Kîð sÄ9q!fð;â³p¹wTé#®ŠJ6fåø†\úlç“,(êžàím‘F|—âÇÝŒhç þ.×Lqw*Æ®!Ø’ÑiÙ7R@Âæä/ÝÃ2J?P·º­ãàv,¯kˆò&ãú½²#zl€ï:üRØ'2¼Ihb—•¸âˆhŠ!̶›ØfKKk÷š¿Óí"¼ƒ$Øs¡H,ÎÅ\èscΚ9šÕS³­šŸ³q%PŒ¨Ç²(Ó˜OO¦Ô'`© ¢y¶£j Ci ï{éêîƒ8éØxÈšä›§úéÓ—ÓÕÞ0€À¤¿_`‘¹BaÃÜ7xÍ  í7ËÞ–H “r…9ö°“;×e]¡ØçÞôT¿SÌ?fÅy‹÷U§*ÜùÙ^7ÀLEǔЦ2‚í®é*ƤbgtÒV/ÄU3ûÃÀ.•¢Þš×ÒôÊ$ù;=ÌÈfWƱ‹©ã·,ȶô¸Ÿ6RD±ëÎCxåÏ™í$Œ%^V2¨ç>bõx1˜–Å®˜˜/l¹—sù!n¿Ì=óÞ† M}Cቀðúeƒh‰6ÏbôÚ¾Žê㼤 RHá¸%¶ŸÓŠ]eªx…ÝyÞvQùéš#z}ÞÁµY„­0š”i]1Ì\Ü*$gyUåšRzµ ¸m£Ñ!’®ì®Ð¯¨hÈL¾“gn3üliÉà{±yÖ­1db¹Ü!LjeÒG-ßvà:2{Æ ‚æƒü-òâNØq“^M ¸Þpö½S½Õ½õ#)n—^Ù|ûÒß5ÿ¤OÓ²¢„J1åg¾ÍzŠ„å±wæzb˜#€F3'Obò^x{ðض¾r)èQR`ƒÍÄÜá{÷‡ÝYU˜i!‘ö¦•Ð-+¥è†^ ÝÈÒW{²šMN®„‘Ä\IÑf,ñ…¶üÙ gp=t‡æ _“+ITªóbÃòúéãh'ØgIމÉÚùá¹ø2`K$ÅÿBWI“‘Ô|“7Œ;ƒ‰Ìòx* úNе-U}jœcˆÜäµ¶›Çk?š¼·}ýƒé0ü¿Š@!Àƒ’“ ±é}µ˜Ô=—FÑûL“JB¿V>ùűޖƄ"ý^1Á;Ï¡ƒ˜«½¡îæQÔ¬=U±‡Ð‰Àª„zÇ`ÝWæ¶_¦©!Êå¡3]Æ[n° ñê}úz3ÇØWÞu˜‚%õ\ ìÛUû4Ñ|äŽçô‘èß-úì’ú4'm,Ó¶c-4¯®oÔØsÉÓ;vÏËóZ3ÖÛ¹<^ûœR̯ݔXò6•ëb=ËÔ6Ì{ýåPÿ?¸¸¼Ø6ÕmO”Õ¼#l={ 7ÞÐyv.Oà½xbé+å3+9ªõS€Ø•ï E÷ßg”éñŠ6#á|&Qù}ZRBB¾jvm¤³ÑöK¼„¡¬"äÊÆå¬!{QÙ*?'l6B–é¶Ô)È-@\YÀÕA.…IÊi)%í\”0•i὘k僕4¦#<ëwðÄÞ- EÄlV’•02qV® #£Î©(:t¾·š«ÆG noM­Û KšhÖÎÌgxƬÛSv¨Ø³sÛ€œžGØÛáB:U\m‘"â2l "&:BÙ é©ÈØVKÜÝ>ó ÜÜŠýN ºç‡Lv€ª¤ yX¬áNwÒ¿i=KÅYŽ2ä5|Ì ø&wÜçó®Ì™9ÖaÈ¥x´Q·6ENdúæN ýK´­—b Âúh‰ ða؃Þ&à5€v§&P~“^]øpŠãaã= œ[£ÀÙæÈ[ªÀìÞpüØ•c(“­`‡µE·•ùÞB»øl¨ÂAäWîá=g6õ5+>â<¹)È‹›¯ZJ†éäÈ#FªüïMÍwú’ßÀ«þ¼óƺµ¯Bƒ“ש‹f4”™kë+*wÏsÍgØh¥ ë€&f*vî’I€.T°&,š½ºUi:5SZ„­#-€|o]/ ¾‚˶œwé\áĆ£òÅWþæm³è–“SÏ,ML´ÂXé`.vìèû_Nfû·Í¹7j;ÂcÑ}wÈë…Õ\œäùÍmÝý,TLÚ ²?K$ýOKºVÓ‹¹9m#e¦Uìߊ‹•Pʃ׀v_!d¥{ØþÌ $DÃg­&’ªÞ–M¨ÎsðЀt‚»¤¡†ÊÖ9î衲cf¿u}êYtâäô‰ÙðœPsªf¯ÇÙ¶›l½ùé3³A-*=àH.êâ­§r*íŸÓ;ôÅZâ=sÍù[] êÂPD îéžûŒÒ~Bmúý”Ú”}ß Û¼ó„=¸æÉ욊|aVª´ã"Æ$΂’ÇžÍ]^“Mv™Ž ð<öƒf¢H¦ÜÛÌ<›Lt¾3dzI¦=^šlê`+“º0¦¬”˜-H äJŽ„J}м·f0ÀóÒL3•ä2¹nü‰i«XÒ­‹rø$€N=û[ê¿—H–àÓîz@Á ¤Œ¹sʶډyšU’ß·thÕ7-Ì]Nqþwt#µÕ]î-Ü6H4Ì}ÞÛV¤l÷°BS×Bÿ<{l×´•žçÇËn¤Ì€ÇŠ-s>E:Ž{Áµ¢EEã˜ÇÖ;–‘áêªÎÐ,6¢Ç CF`w½NÊj6¿$¨¦8w¸âèÕÝø-!6—Ï]¦®‘“ÿb³Îa°U!†—2¤óIíÍÅø¬ü™ègpßçœ\±Èí~&aÈPÝ«˜cÈ52ÒN8Ï6/zÉÊÇõô8þ75Ñ@Õ^«±Óß¾…VàC±ÿ ë[ñ¨T+«Æ‡Š+Î*®ñý­qÀAðª,ù‰öò¢ÞŠg> Â~ÚÍþk!dª$šàüÉÌœ]'fÒG §nt¼¿}“ô|ê¡–ÄØ–@±Ä¹0‡£ýÜO¥GNëÿU‡fÊJÀ¼7³z_™¨ËUÎòCqh >ßõëõÍÕÐÝ2- 08§ ˆïíp‡žÓ*€nj˜=–•åïÁO¶î#p$ùc.å—ˆ‘i)˜kQ'Qô74;\ÜÃQÊ0ÁJ%ñ·a‚-`4ƒ5"VŽ'ÉtðÌJDøˆXÁ¦î¶ æoùË÷<ïÈ[Áz+‹r‚…­žˆ·Ûn²AŸëñT1§)襄=^ˆyyþ¥+ЙN0ÎÛ¯þ½oŠT«øsš´íxi‰;¸þ®Å IXöj4aç Ex¹&x“lþ/. fõ=dHZÅŸCq 'ezB•G}LŠ„yÆ*.çb"ÙZ熣ê–î¸pú°É·yÞ¾ZL'SÂdWs{U2 ’.;ÑJ§©‘ÏBÇR¥¢Ð—€|‚èï¶µñY1«Ê¥ta­ã »Od æ<âÜl~Q{éßÖn «ÒU¡ß¢6ÅÖ¿yû¨l*”Š&‡ç“[ì—Ö­ÌDÁUk¨Îûï f kêÇWÂçI‹Ï!ÿt0ÁÖ ,œ˜!~šÝOèè ý MžhÒæ/2 ^œYGŒMVƒ6㣖„аï´ÚDš4ÍÉò…l¾VŽz>ñvˆjo£–EÔ€ð*viÛ¯ë‡ïÙï÷/’» xö"5¾sb¯'’s¬W A/؉g¶JÒ¬Ó÷òw **Mq?œ ·l#J¦ËÅ–ìt1Žjb±{æAñR£†rhpø"u8â–U*sìæx÷©’òÄ“}*ÖΧ¾lê?ËøŒ ;âø™E•5Œeð¬ ;–ú4ÔkRûYôˆ`cŸ[t{OWhøë;,„«¯‘Ы>‡à?)’ëòÍ+‡º ›º‘B:‚¦“»çë.ϯµÇ¢+QðO…ô¬Ä ´ùZéÖ÷Du•T›ý³\4ª/XŒ ²Ä(‚i¤ã0b4¬’’Y­™¡àKê,ZÖ;Ð1~pó.l´“Ûªxï,ãçƒðç5m+°´è/w]ép†OÙw,ÿj+š>|z@R÷² lÚo¦]åÞÚõFqX௒î.˜dö©Iô%¿Ÿsñ}§[ñìi(Ì~Fý(öŽŸ1\ñ÷8O’‡ø­ø}ªjÈÂY6ÇßçÈ7 D·7ÊÒµ?Þuè´Lk(, øW¦²’4*¤ŠÞìÜU"UÑE1"Ÿïû!;ø€9DU ©l}v SÔ€€.#ݸ;ZÖe*¯iÊ œ ²tÙ„e ûçE]ÛUyj1U;å;æŒÀÌôOЋɩõeôÌ7îÕ‘‰l#m@oC¾'‡ç™×(¿À»çç¦ãä’€Lpn³z‘öÏW9SæŒx†A›O›‘ ‡ùQ„­¦pàù¶óò´Xßʰ‰ R’ë¦Z)»tÞãSmö]¦L¥5Óöô¡ÞI‚­ ”jþ÷\ž°CN¾Åg„Z$—mzyi|=^AWA½‘[˜´yî\B<ðô9Ɖšöÿj±ÕŸà‚4’?ZÃr/(¡[E«¼_÷.Ë#Eä]ßa ÎÞUhè9—û§'Sç¨éÁÎWßÏ~fFq©]LJ½êç¤PŽ#ëI2©îuŒÚ׋|Qh^N€GÝÑ ˆ¶¢še¼šA 8ôp¡#RV£ÞuSŸ&B]ž¾| žRq+mÏAáÚž\=â {Cº~rH¦>D;üP˜dÈÈÝ/q!7ÅGÿLöoQ¶†ØàÈ•G%È’4º)ö} ¾éäd‰†Á* Ô–‰êçâjÅ"„ 8 ’1l¤«Ž›à°êÜZǨý ÀoSÿ†ºZéñvixÒꮟ7`»otÞåêl¢ô庯-d³ß@ BrÆ a6S6áD:5tм­à¼ûk¨$OØŠ™z]ZcjkóÛöi0§×'FÞººr½]yšÑfN"Ãrþï]¶rGõ]‚˜î˜;ú¯oW Oôˆú§6¯LY8®wx”ü´‘+%HÎoT£÷õT¹e‡È´i“]•*O eÑ%l:#×±ÿ!ÓK?ô•žºgYCtOŸöRWÚI d‰lmyüý<+ˆÓù†)7PÞ?»eRô3íS¿ýrG¿výçkw*t2èLü™íÌ´Þ㿞áZ›l™¼P¬Ë›áÝÿ,ûÏO¿9:I‚Œ“B‹Ž)ðëfïu–6-@òtøt¤°·­\Œz†Ôa Ww^Â_Ô.Ѧ킭dØ¢Âvõ»çÖñü—öÉ­ wdmè  4Zj=:ý0K<<ײ¬ñYÎ-Ž.wÐMÂìiÈr¨>2|°·:O¬çü.h`|w·Ÿ7¾ÙmóªÃÞ¨­|=)p9îšåö¢¿Ç}KVÞ_#Úðilw¾ð²%&¸nãèïäå¼ý*LÆ]r÷l€™˜Gnþ»é ø¡¿™#®NÔο© þïA^`«®lèæ.FƘ|'+|G㹩z/g±Žà5-7p!â´´¢ëÊÄ ÁŽ¥Ë‰ åŸ8ôÜñÇR0@ZR v²@$Dk|“Šš Nj÷þí+]ÆiºŠÀ"|eC#¾›Œ Q™>ÍÍ:n @áCû¿[þ»¤+4]Î`£ë§Kî Ú]”Ĉÿ„D‡7¥ 庭±âš… »Ð'Y$cšcp)¦ÑlrÔ =*ž¢ë²g‹D ÏO©Å‡ÝÈ,ØÀ_¼òaûRúëßÙåöiÊck™dpò’‚&Ón¦T/ªwƒ0áû¤rN9åÉé*É3¢ \‡KàÆÖm b¿Ú­¤–rÛt p•’b¶ªê™†i÷ñÌ+§Ë¥µóåË  ÒE =¸ðS i£)P¥ å&¾~xe­÷FƒŠ¥#°¯s"&»ÑíB†kÂjžIíR IÌþàe›Ê ÆHT<4ûu¤Ù jS€.¿& *’­:\Ý@Cÿ"‰G{že¥NE«QGq=§ŽÏŠ?ã•#ØàN¨ûVfLiÏ9·cé¢X€–ð§÷PSÓ‡rŒVá3¯ªMûp%•ƒÀ†=­¸eÐÉ÷ùæÜt|ö$Þô•n“Ñ·CLÞEi5±},æ0ñÖoäìÇèH¬kzO¡‰È$ +¾’çñùJ|Z9ú™Ù i*6C7ï«©`#ØÄžK7›QŸ«ü<(}=m4ÔÍÔ·de¦†Þªô9»¾Q›ÜÇ$fO–³ZèpZ`ÿp¥A‡ód1ßÙÝž/ðºN]6{…™±‹GÉßøÜBo,wñO͸ ¥qRµª]ƒÌ¼í1´EE!' -2¥Â ¡?ƒP¦³››VyÁ»Îç#ô1‹ðTOv“-üCrL²Ÿ{Áøì ‡ŸE)Àƒ‚Jêê­@Ið$<äÑ+^kCKYîϼc "[«»QŠq1{Ü^¹_o¤õcüñø ª3–ÀcÀ à+bòN"¿wÅ8±g„SžiÔ5”œ.«­?[IxbìÕQò›ßt£}rÁL†Q•êé-aÀgžɻ¾ˆ£;9oÇÆz6n¸÷ ‘ BÇØäšÞÈáòxD<ÅÛ¯+¤Ÿ-UI‘‚$-öÿ*Wdkê0¡Ç“™)²¦ù€¯ð“qÄùì¹×dú¬’pÌç“Ư̂I0¾÷Ó™ið€·ö„‹ˆñ8;¾‘ï~ =¼ÇÓ­A¬ƒAêý¾ o7ÿ=ï;f`“Sõ;óaf¸H²weç*ÕþÀ1Ú‰œ»õâãÑôð:FÔr­ßæÅgøÔ÷x™VŽ-Û¤õ«1n–…º³áˆ”o±×+Î\^¥æ¤"™ëžX…'qI¾ÿÅ)f·q£ÖK7†`,KžN´jFêC¨·¬A«›;ôÐþï5Â6MAoœ<­8ºâÝÒq¿Ö›÷1>P‡KóºaSÃÃÞpUÀL3HþP¤s¢Ó ‹ g0äv-ž×´Líê+ÀÔÀjMDóOìa7w§A©AÆ(èÃzÂoPœCIÙ¨ËÝn0°ê† )Æcû(Žf«¦4ÛÏÞÇÞß)ßノê…*’Ž”ã(Ú.œ™µC`œ ïÆ1%ÚÚ4!~'—k'ÅmIÁÊzµi„Xòët“>GxûHV[™z}~ž‘œ~ë|x†¤ÚsÞ³Ÿ8&I„÷“¦ºr-_Ô¨C1eoÎmïdì­#\MéÛ±6Ÿ­•øš8&¯t¹Eô.4*nª{;äË oø‰E†‹NQ# ~“Kª+ñ¨Š©Åè=°“·"òCu5zJ:\4¥dq=/VÍÿ~¡¼V’‰nTv“ dŸ=Pp¹¥æ‹¨‹V•\™pߨ‘­„`ÎÂÏÝÄ©zíy„¥³>2í׿!Ì·Íçîò8Xzƒ­LN$fbHZ¿õº«¨xòO-Z)n)_Ý› WrÉCh;¸|ÿ7Ö ˜ÚŸbÍUEjIí±¡ïÏUIÉ"c˜¥7ó·ºÁÙ3êjœ‚¬UŒ§&¾¿Œkw>/Ýâ‹€Š†T#5Šü‡¡—žÖ/“’>¥‰j4S-ß/â7óx 1|> å©_ª:sª–¿å.Ñ:¾ƒÊc˜ÑÝÄ|¡®^à§jÏyÙ|±Qª½ý ¼X~„é=aßÁYYðtÉäJöäU ŸZѽ±×Yý»ÃH,'y„T[(Êá«,ZŸºMå¶ýoJ¦¢;˜œ×E¸\×M3NÕ´ëSý:ƒF+ÔŽ“ík¡0l,»ãœXnvË&^Ò0oŽÃä¨Ü&•Œ|1ÜjU£V¶"{€së× Âüfˆñvc÷H„]ºûøï5e>&p,®L©Q¥çÜ6Ói#ñrüL^ÍFhOW„„¥¾ ¢;š“ 7/ß ëŠ!Lm7¯aCh¬>Úþ yæTm;/TŠžáùW:bª=¸Èì"3¨.O.Òä{Ä5/¸%ñ —±æNý'ïæTÂì`ö3ûb4Àe;º2¦bß-IjN†cšÙ3+êf–HÏ"¦j$mC6¹Ì„ؤmlŒ òo¿j¶&êêc>òåø•ZÞXƒø³nµ¤`Qi1®fO.¤'•›Ôï¥V± Ep‘ÌN»{zI4”æ)$Ç)ÒxÑÈ"LËSdUNw+‡©¾~©9æAS›ß\ÊXþïZ÷Þ)áÌY-þ]˜¿RÜÌ“ß$çÁŠZ{\ÉÅÇzòæ Ç9Æ_ÕîÍÿu‚ úèÛG¸{ÓKA&ï0xTd»—;jãvšÀíeg¤Eù¡Ž|ñ`«Ð›Ý`6Éð¯ô=î~÷"†$lzk‚ò—WxO”°iMÀƒ5ö^ wÕríûlËH^…'Eú§Í§Îf]õîDÇ»üó©¿K “î­d’W˜Ó3sD+²™v»~3ñ”úÿhƦòªôoæÂxs3û‹T§¿G-§F2[n¥›ù™ˆqxé»âýuºá¬èxm VùQÙFf±<ˆz–wzbµ y ‹Mü²ŽÇpš>½éœÏATÛÐN a@Sqs%”¥¬CÎÆ|Ð8V ü·a_H×9'´±œáÀì¸EËž4[P/„øŽ¯ù©rüŒò¹Bˆ Ö’ (î’2YfÊ\æb/w„¨ÇdØß~j>ÚÐ@âcLÑJ-£3w@<¦Ø¬ØAs.—Œøß =P×™–’±»wÙ,Ìäý¸àt—–Оkb:”‚DtâÎWˆJ”õÀyÂs(UâiµíCB¼äÆ€#ë$À{˜í×!0W Jbe^²‚\Ö§rÚ·§©z·IL @‚>Ûex¯u¤¾·Š8³±“G»Ý½®áº0Ù¨!ΉU°¡ŽJ'¯áª„ñTf0’‰1]¯iÇ»¬£Ö¢/UoÖáó·ªcd@áýpÇïÕÅ /i­‚~ÄT.Ë‘°¼ µžWî_ºÅÀ!^Áïî¤WÖ.‡] ck'ù¦·¥>©l榮ž“6(Nó€q]<Õ†Ñ{±×Ρ6sll% €™ }Õ:°®’~ìWi)mÁR̪ äÈá¹QNîKq%ÇZCpQ„ȯƒRçœ&<¦ìÄL›ï*z‡el[a(/$7Ôi¿xʬR†ðÆ:W!¤t„áÿâàç.àjÂU± ðËߨDo1¢žêú5'Ü‘Y!CÕb;èñµ±H¯ûUð£Žç´MÐ6Ÿ«‹™£ÊsèÐâLi³ý\—µé>$ÇáÒ–«í/ß„“Ä.ÓÛ2d+Ç©îyë©´n©§— ¯Ýûš ((уaIi‡©Sì©Ås@ï#¡„|T ½\šz6ͼje`dÇw·Ë«µuwò¹q;Gº` g ›Æ^uŸÙ–>v㎷)¹<`¶$’°YÖ¬)ð¯€MQ;¨ÂáG؋Ȓp”w´ÈÝI{¬Î˜£hƒ5Fo¡pDV(pC^ŠZMˆ|žx¶¼(¡´Ý 7òð$E¥KY^4j¥ ¢7å¢ÎÙ¦¶]ÆÓ‹„»!d•åçiñ˜ñåSÜ›ˆËùÏ\'õ·…ëv½íV—+Î"¤cLqH$[Ù^kµ#}gL_¬C«’¥_ÛtH`.¤ q"TÈ×ü€”íÏDÔ;´Ø© 6¼Aïk¶¼gÇöYwUã5($•ÿHºvw&ÖqrÂ…/zJ—KâÙöò –Q»eÞÃ-êþZÙ¨¦FŒ=¦}CP*z 10êQ.¾ƒ-«%‰œØqeËêºeáÓ¡CSìi˜r¼sûõ¾¶ÿ"³Çw–W©Ä‘N`ž+‰Ÿ4G®˜k§D&¸X™.·Ò AšÎÄÞî=Ž¡]ñ„u5¿¡²V¼Éñf¼û]†£šó>¼.MH5ÕïÇhÊñS t›´@ Ø*h- Qn> q/›•žåzºähŸ?‰`  (öUÄÆ{Ûevã"g°¡Û·ˆªÜB!‚¨­`cØ3¡ûÆi¬¶ PàAUTñÐ ¦³‹ÃŽäÁ ¤Z¢µòx~ yËáK»i|S…Šž†(¬<7f ¹‹ƒÙ´Zb|ó‘…ÊŸfœòÄS¼² à³™uy7-ÞØb2ÿ^ecÑÓ*e³È–mù¥ŠŒà û1™OFÁÉX\ËQçoœêa ½z÷ÑäŸ Aî „FÁ_Ó¤Êò&Èüž]VtkôÒDó$ÁtseÛ^ñÓ Û_b¤Qu‡Ç$ó¤"pÙåÒ.áVÓÝ ç+MWîô¸ ¬Ac¼oõË&ï±ÞòµÛ'GZ]rìÛC 1¬ã⨣ Ÿ»íufõèhhØðVMÔWñwB\ §Wñzä0F fÓàÍÏ}t"®:op~«q){·\åˆwh±¢ÑÊ"¨Æ÷})gTH¥Ðü/[|MuÛ¬[Åw½x@óe¢K{'ž[_äÊøc.h(žýx‘cfí|­µ†K‚ƒ`„Ö }¦Ž÷:w= l±¡åR5ÆŽÖ¢ú\úGÎ@k}Œ9Ѱ‹g:>ùXE©†EÛmŒtV ý µ*ÛZجÛÈ)˜PLÊ|£‘ç‡SH˜eÛ¶5.Ê·(ûóúã,è! d÷…]ÊIÇ?Ù6±Ñº‹W]<ë„_‰Ü5q°¬œÐ0œ3¹ÒB•°òCŸ`Þ @&Ò»ÏzÌe‹Egب2=‹+P}ÒØ®ÆÆ {õà-¥œeU¬ëmf¤âDñ W"cëúŸõ/Û1ßéœÃ,ü ³ »öîúéï­þ6+¾4ƒÕ |ËÜ:X¬xtªåšœÀ6L¬ìÿÈT |±[™x°7¶8Ù@l1.$ÆLÅ îÓÍòY| ©èG‰ãÿNc9[k€Þç²0ûvn@QøO–2¿}^»ÛÔTÞW‘l/#¶|Æc,‚'Z?~Cz" /!q¢dièaôF hF; ¼vC¨Í dç…€ÿziyj„¦c(æ‹ËCñýlg¨/̽~ ²­ ÜQB\àk÷—³„˜fb–âf‰ä:ñ/I%K!Ðs¶>PÍ“ÞȹÜó™8æP÷›c’¯–™JþÝó÷­fä¸<ó‘æÌ!ŒWß;ìØò87 dúx BržWu~Æœë¡ÞÊ¢u®~md@ñ¬”;p]AŸ„[&\¢2,çÅ' VË]Ì3{x[¡ÛÃý1»ÜUÞ²³ë Zp)@»¾caÎØyˆÀÓ„b,áyð¹tÈ-íL¯rÒ¸“­Û\ÓÞjЗý¹ºÆž‚h]2‰£þرæcÆOÇgÊ—=1µ r¤á„¸UkâÈÖìR\Χ9ê~…´Dã蹨¬™üà†— é”r¾äÀäìÇ·*Výh$ÂöùŽ#håÁãé'æ©bfØ›ä¥ åæ#÷?âÚbŠzòÉåê 7Ó£ªò=ô ñ©O`-×yÇ:„6ô¨ŒJUtz1@Ý^Ì@ô¾ÛpÆ“Z4I–ASçÂ,òoâ ¨Ù¹¶+ªÍR“(@Ú¯òÂÿd`1áb“×בét5pÓEúÌŒ†‰oU¡·tW×_§W‹dx $³¤p+0Éÿ ‰Öò§ÝK¾‰®RhÙNz\b_Êýh¨L€iø›•ûC_ÊÔµ&AgŸ'½×J$bà÷¤óË:PÒ,¥?ãáªRÑqô2!Ÿ;Œh*‚³·­dWŒN9R…ò¹¬RíW òÙ6¨åm VØa˜:¥É1‘¯¦o÷TKpp!ûw°æýK‚Ðg%'hÿ‹ }y?Éöe1½’f^!J!N9ŒçiӇΓÖ‡3ÁЇV-Y3CPþD)ZK4 òýøØOÔ¯OýsƬ¦W0ºµït™’H 6Ÿ9¡jÚ;Ÿ:‡[Hʯ…ë1ªŸ­m¬XÑl…¢]=)`Ë’ÕÏ•º]–$íüŸ28µOöqÄ– ³Íëê ûMS}j$é&»~¢ñpØG5Jú"ú.ç3ÁVÏrˆê$W­‘{ýÿ%pñÏ&`æ ­/å.<ƒ³o¨6îÔ†óˆ[Û€¹ŸåûÝF8þI4D†2!%ÂÚü™$'2dÐjp©>U:t¢Kç¹§\3r´"ûƒÒúCEÏ•+ê­\í= ã†ÝT>Û W `ìµ:q‰cöè·ÄíQ@Æ· ¯•‰K ŎO\Y:%òpYQ†±hˆë¶†–P‹äתå"wúûà 0¾»8Ôeó<5ý¯ À©n¹”uKÂéXñý7†› iüŒte ‘5õÍp˜èW“"ÚvÖ8ЕrÅÊ繓æ×Û^¶êÐôµcd¢´lýô©©]C¨•gkÕ?¤.”œé™G൓ä@NztŽ'Yâø¯Â‡ÔÉ!÷_ùçI¨[J®šÁ°<ábw[“û`9Ö‰ýãUŠ.Ÿ®~ÞÕ=^Ò¼»µ]ñoËâA\UëTbûªXŽë6¶‰8ëèɦ±ËËLM/e=}sßܪ_Þ=PXÅwêÃ&ö¬í<¥O‚ÿ^ʼño„AúB¨!Th;Ôé|\­÷¸&."êˆÏýј¬à2¢ê, }•½° >²dš^@Àî=£!)RåG²®‘j[h.Øg=ÜyÇ q²m˜Ý°¤aB‡õ—š>,v[¤{Dqíg;†/·Ý/ãòyBÒâŸc×b7HË@Fõ\ÿRŠO-éí9Ò‚Ê®k˜1·\) ™ÈÙ’K!ôÉm„GIc­3¸3Ê ¬c} F€£05=±ªRbl]Ô?×(Vx^ÌËj}òkoõ¡ÍÖÈÒ•H!Özm ÷°ÏàC˜fJêÿ ßs6guPðŒ¡€ÙÞ_¼½ï^³¢y¾º1ÿR"‚{7ˆW¯ŒR>TAo¡ÃQœ~û—°B0Gœ«uøúô«– þQ+¤y¹ÌêøÈ¦ ðÜOYJ‡œ6¥ÇCɲÔm~êÿ-Ym­yõ®#³DøðA!ç”ëTÓÛ×üäÍWˆrå[fªø¿xŸðvÄ,:ÛŠP¢ •óþC„³GŸE±$•ŠÑ×Zó°ù¹†åƒl–È1]-!×Ä)ŸÍN¨ªE5Ìžˆ*ÂñŸÂ#QM§.Ôˆv/*‘b/Œ­¼CNA[£ÀÝ;Áû¿:È7ú^U¿pÄ@ÊŽ3øH’,çþ˜:¬ÔNÛâ?.Ê‘€dä¢ì³BHžÐhŒ£l*œŠ°uWbk¥ryq,¨jîs}­†ŠF%/ï}Ðvfb+“Vš÷òúÿxU_ä µ5ëÖ ´…¬ Þ(r )æ\ â/xå‹`Zï#ˆ^tTT87í¶[C#ÔbÀYƒc×,Mïb/“öö‚Ýþغ»§7‡oÛ ›½Ÿ6µ©/2!ͲVT”‡®ÄP³3¬7¿Úå¸B!ñ Ç)g9K¹à„(i&iÇ$à2M<*Pƒ=*謨5˜ç%r¹{ÏJÖ…¼ GÃL£ÍùçÉXAâ6¤óc‚ Ö à§øòÎm¶ßöY¢§žsˆ§³¤»·%Ô­á÷ÔF!-0G[œØ°# d;†çÜ­ó®ÂÉÐÅÃïtŠïïþå„m71u”A€°º™ ɇ¢•ý;Î=Ÿë™$Âo—][»ç³uÈß»~™ÄìasD=»¯Ë¾(V¾˜2™‚Ws¤IŽž£TPÔ~Pú²ÑÁl½ÓbfI¾áÑNËmo÷Ç4w¹iW†¡$ÔÉaäsAàÿMÌ`HDƒ›J—ý6Kf£„RtØBKxÐ1ÿg\׆´>BåVÍÍ;¬zÔ´àD×g©° ¹Î]8i´d%ðñŒfílV(dIIâ)óIvÍÊS ].¨Ü)¿Än@½IÆ1Î甽H«ð>Gz,¡ |ÁZœÃú`d W’kí)#ëÕ‚jy8]°t•©ï[”š&Ðã\˜»&’ïô ^ áÃÃ3‰$¼à(–€P ¼]Ã_£ŠÂšb“ O¨ýK¦¤Â ² áfôÜk…=3bŽÑ¯ayxË‹òPë÷!‹)r‰UÓ¤Ò¶fRp¿\„ÏQ'uò;мœWÝ ~´F…ƒ b¼—†/;qýH&ß_Ó“ÒºmdHÉî±Wš'˜àW&8!¨™Oý¡‰‡ÿúCÐüU$©MÍ0û" }˜mXÛZÀ‚Â+ЉÙà¾UW|°I¾LPíÿð(ªHÓcÿ3D‘x%Ó™eáeH4gn±€¢ÇC%@·býƒ—àZ=,Å;ØP·(ô1Ÿn† àa+¶W q9…è\ËŽ7„qjÌçž#bÓUt>Cãx0Ü_Æ?#=±úÈ€V­E¦+§ö”NºÖâ+§S‹lpZ½B#` 5›öüUõ)x]õØÉçXt^+t9×:©Ð$Û¯«š\kÒUѦ+K!#höZD¿¤„f”?À˜1Œ]Å]úÕDôÞ¬Cr6jÐÓb ,rÑ©z·¯$ÚÑ=O†Ýo`w¿0‹w6ÿ›ârr‰É¸¤’ d(È.TCÐÎz'”ƒOhjÄ5.ߪ”(Ê`ݯ=))w[¤{㨮GÇ+ä¿¶ŸMR.¦žãÌc]§Äí›kt6…ÏEåe'¡Z,s+{¾®úkp{£ª²m>„ǩČ=×ÌÏ·/Œˆà÷rÒÊ¢iê¶Í Åý«k/»¹@ØäÿÀÿkãæ¾ÝŽ ¿Ú 'l9iÍo?aòǤ7 êò:vQu>…ù™mìË:Fãl±ìU…ÿS¯¡·nËFWñ2t!ø¹iAÔ´Ô£„TE~‹Ë{}VÍ9žËá ÇCd É©æ,B©U,ŽO„ §®Q|¿5x£ñæC?gR©ðóÓJE¹]§M„+;{ÉxÚ…N=Ói…ªR9°r&USKgay˜aÂÿc™0/LÝB/ЧNÔ,È«úk5€­O×Fqλç(ƉhÑZSÑñ—CCpC*ØB²A¹‹>+a¯Œõš´®Y+Up¤WŽt‡»8EŒ‘pʹ]§víƒ#czøÊA³‹&²‚×Èàäå-…°á–ô++'ès?ìVëpã:"6 ‰~]ÊUFè V0î”/‚³°þj3 ó]Ë÷  IFN¸ôÊjvt˜ô-«±ªÄü¦‰m£~ob‡˜¶H˜+g«„‘ PäPñÙþ¯ûO¢IÃwg+¸}oIG`•[”D~0~èß½aQQŽhQ¸á):‹?æââ‚vÈ orMgRöø:G‰ºØ½¿öÇû~3‘BNÛÞÄ™*‡‹pQ¾ze¥òAåÜ4¹›(ÉK*‚žŠ¾C=ÝaÈÍ›u†yË{»oø‘¿³v”ý‘½Ö42u2äáûßIbì<À\å–”“úñ„!PRª+ò+ œ©a…ñ²¿u8sV²Ê$š1[‚¸JD¿R=N¨æbù¦/ƳS‚—Ã+®t ™QÅ¢T­¿‰ÏnÛ_êSLx¢‡¡¸è­$¨×¥‚µpét\†ºF q,Q!X>Tô1e³Ž€ßÄa|i èÎBú*wSýåãìd´ýä «í¬ ¤¹;–Wø–0}IM×6¥ Ù›¥‘~(!£N‹Eˆ$€¢ÉgíÕòõôíM/s £+Ó6Á >\âÄ­£A–²œ+wþ~ìC€pÓËí Fr˜…û5dèñËî‘’ÃaÜ3 8“Ž ¼2Ä`u”BÈ!hÔû ©ai‹[q½óN¸óÅnæœL^‡8 šAø>¦˜ˆ¬›Ólf³«ÑÍû,ÛUØQ 1x>-3´-ÎíRR¥ÄÊ=SøëÐ3Èš©£’òy>ÈžæN‰¶ ÿ(Ýåz“Áìâ™Ê>­z]°eûôJd9G2ê„HìŸIÄSݾ¨¼¾Ôàë:‡~!š®õdÃF9AFúÐ"F^—šÃ7öaÆ£—2 ãÌŒ?-oUðŠEŸEý Ü—¤…O‘ºArDó—ƒhmd,*þÑS{¼—«½êƒÇ£Ô䂨¿å!×dkU¿ ^ Íu7^9GèSû£^m(¶ÄšG•Ç>¢]˜{ô=¿\c†úô…ð¥X[ÐM5°a*µ´E[²Ë@W2å/iŠ&Óô·¬`Ë¥ƒæZùWGa `šêljäqÌVš¿[ÿ@æe z5]„ø-(Ò™,c([¶^ÝçV‡u9¤ŒÏNŒ‘€?¹dS³°í"¾ÈoäËÑ¢Xo×tÁu„ ±ÿ˜Éþ*´0Ì{>õˆåz„¤B÷@ùX@Â|“S„6O¯`È!—DWäßõÂßrM­(¯ý~wVÝ=FnÛ¸êŸZâãNK%\:GZl_…¨w¡¤ƒÇE±[tÓÃÍ)ÛÝÕb2#÷'ð%ºõõ+„Ö'XºÒeñb¤&Ñ·8,1†í&¼5ª%.C÷‘ÿ']êúŒÏTƒ¡o/$ûÌIbksü¿ùV=hR\Ž®¼§÷»Z¡€LЪÙ/j;îÍ †ò/oJHŒt/j¡&ÍŸ “å,ÔO~Âɨüc?Ï>#‚]¼z<¾Žû;h“C_EÛ .%Ò³ 69ÿåAŸØL[éQIöQ†-ЇÅ‰¬Ž\dñv1ýÜ‚Y Sž£ÉpÒê=”Ûª˜Óm/‡›(Ä+|öYrìÙK§ðÐùûó›.aÙ§$j?²&FºOo¸W¶µûÊM1¬˜´‡4¬¢«Ù–À¿Ïxî\Ӈɼ‚’½`&}"¡.½–5”C©;û¥j3RqÜ=¸“$C“Þ€î§ñö5ê-yoõ—38ù<:;X¡$Œ‡ü|gÍz( ÛŽ`)Ô@KnL8} +eZäüp±Q§À’VOLõ-WNÛŒYªææ%DðbS¾@hH¼®°%ø ‰÷ò«m¦ Çÿý¸Vû™zn G ßl½E†Â”™ß’9)º´¥fÁÍ  Š.úÂywXFDZn„s êšÙè’)»½µŠ0y‰™¿Ì¶âwŽ&SS“ÂQÜ›;sfïöºî»úrx’7$±lû{8͹¶h˜.›_¤¦Î–·Lá#tÈÿ ÔkNŽ”rërJ$Y{1V6P¡/ã6ßÿhùá„Üè„›ü®à¹„zq¤Š#‡ÝК"ýw Ë #¡é&Ò&Dzl}%RÚ|¼h—2£É§vá‚1~Ä69h]îЋ{Ñf]w*"$ƒ3(\ŽŠÑ´jîQƘ”Tï{ÑH‹îò"|E-÷É5Õé!^îßd˜ Û5MœÄyc¸bŸ2÷;Wá«ȃØ€"_¡å”ž_$Ùûµ!;¥ ÆKYUÑ©êgÕˆÔGi´ÑÌ—Œ¥Þ½8iÒN< šÄž22íÈY:Æc­ h½H­'V‘—&Wæ5 Z̳UE°[@æÔKm dEXSï„ßU™ÛK– 8±R­I2òPÄ@IÅ‚tá)ô=ƒÊº[²4ÖxHáà´K+SKº$³OX.±=ü=r®hF䀃³4i‰¿|(>ØëK r;Q±ÞïD²'`<"ã‹ç‹[§ÌºÖÎ+¤¤t<ŠÑæ,3_-êñèjs¹‡*,zm‡ôàÂCÊFþ‹Þ=œfff6"®Åäd¼K—xÁsJšEåi[í^ãÁ¼ÍužÛHAWnÜT|iÐ?—·†¬yû:7ÔûcÝãLÂ]Æ #ñ:„¦«Õ0?‹DxÚsFž ­b]L]A½a… d#1“ûӞÃ{³tïz—M* WNí¹ŒU惆`u¡ZPÎ?±?ÝäÜ,Ìÿ[PU >lKœ ;Ö2aáõ¸=ýjûªKIQ•Xy—°J2Ðâ<œú“üÃVDÂz`SXí=Ä®áUiîwÞc…gÍæÊyÊg£rÖ¨?öD<ÅR†ç§K WrÂi®€j§y¨¹»ûy©âÝÄ °s¾}à@¨­«£´1.,lZÿK¥‡^d|f^Tê úx‚95Dæ`Ê©ÐÀÜèÇrΈÞr¨"œ³rqJûS¾'8âàÚ¯¿ ò°0Ø¥0݉LèÒ³Ú3ódÿ kGÐ"ƒóEñ>ÎO -ÚðŒÝópm²Y?[бX…’è[—0Krd­ª`ôSÌêàµß#VGM²`„úQÐJ]6[nmÔ/ÒååÞô‰ÖN‚àXa}]ë›Öñú¨¬Ÿ6¯ÿhy =¦ñ9´~?þn 6γJ3çz›råHÔY°e‘ÀÆQ >t‹9J8š!SÝ"‡d·ô®§KóËù—sʨ±Ð”Þ,;š*—p€¥ŸLo~Itm+Îâà3i({Ôc4Êæñ u©4>]±qy€ ¾•3ãD﹊M¦S¶i“F}ì]W'zÇn]¤´ÖQ¬‰/dã[wE§Ó° 2cZþÖ˜Ñ]CP„¹InuUâ#,]"BÞgph¢dM'Ö­ÀIÌ“—ÅøK"L* ÇòíñØL+ôå&Ûž‘%öU¼×GDd+õâL¿Þdh)K€#ùÐÍü*È(1›­Ã6eËîŒFÍ1î­¡ë˜"t.R• EA!'iˆÄ&ø…¿E—÷ˆs×Ð û¾M¹ýîÌ“ ¦'èjoµV=¿bg£í2û‘‘ô™]ȇÞKæǪÂË„+ä<1æµ°$³Ã0Ù€Ò *|.6€IA•Y†•¬ užªÄ‹ŒÓ\9†}=`“º$‰˜8§Õ>Êw~-*®¯HJ¬xŠ-8Š%i;3®¸3™iMKß\'C€øÑúOän­¹‹U«Îˆáøñ(P}ÿq1Q(f²7©%pˆ7ÃVĉß=×à†ÀéÒŸÑ™0ec¨Úa ­ü\ó Xßh„IÝÈ• [SJdzÄ÷,ǧ£Ë¦YéyÊzî΋öj¿ >‘¹{OhóÜã´/!/1?1|¡Æ%§nFA$„óQê— ­‚¯6Gt>à݈ìE§û}An-3ï0r¡w,P^£3/ÕòXÈÉÌ›'t- Çñ-Nש‘ëEžÕröí?÷ˆCÔ±¼æRXÔèXÁÈû1_Øw lUkºœ7lã+‡øqDxºr}§jµöÓ²üWïh¢øOwäIhLI» ѱ}( ¢P7¼ØüËs“‰+gQ•Ð[13§[V«Úe|f^×}ú>Æqô9®#LvFs8^‹={Y=Öå„oÆʨæå}ú¡ÐÇŽí4+¹ó%8ã/“JÆFÚ3OGš•5:-+ö«ž*7ªmÏèì‘àÇŒµ;½󷢊—°_”+ÍÂP™wfƺ¡šáÚ_{˶œN&6ËÒ!ÌWGHå'Äá¤uoF@EÕÀ­Â?šp)ÃÞFèVÅujQ£—߯/ïÉq«F6优¶aK ’tÅÄSÓª—ê:ú3ãIVÆ$.„‰ ’/æÛ|øu¨¹GV*ØÝJÞVj½q"p€Åœ•Â0«Èãs¹õn8gWü÷!Á—ëæ,ˆŒ"mX2A¤2¤s60Iìü[ùCäLa)ѪÙaÛë“ÐXÌe?¹Ûª—Õ~|ø?‹Ê™˜x´R<3}íÁkVîH œLu<Ž7@V›KeqHoËèÇv›Û›’–åÇEþê>»WòÌ ¯ÜKVU¹Ûhr$ˆ\°ažã9ØÂš6Zä)PGçéù¦DWoE¬0’Ü(%D2Ãj–ðN)²¨ÍÆ'—_ÒåϵÞ;F.0 |»0³bÆ.vÓÊÚyÜj[ÂãØ"Æ XzàmbJÔª—ûñÔôàR£W–s]R#ð«Q¸&rñ“ða®ÓR!á‹Üe©)6íÑ(L'O) ©¹úìõ˜­çÖ†MißfFÙybATv ¡¡:²«…¦j É; Ñ_¹—£Rýã%`ÏØAä@ Hz3¬äxãê–§¤²n´ ·íÖ¯¯ò0…B¶èäUÃ6¾°ØL¦”ô©b04Ø.ÑþpTLwiÊ–@*Õû]øÆ¤î–Dù¾`4@Î|¾tPÅ\»É¾Ë©È &Âå“\&6‹ºGýÁ\¿ø-H„10;ñbl¿|ð2Çõ1oÍAuä÷ctŠAN¹ýˆ¤%ÙkÓ*Pá?t)-Ó»–ësê×-ªz)å’ƒÇíŠÌ'r‰giÂájËàæÁê دµèΘÜíŸÔº+âÔP´’³«÷Mh½f{Bæ‹jbóœî#·êò'„0}Åo©”™ïÈ–GŠ}°GÂÙ÷鿃Ä"˜ ¿¤pZ‹y ôUœ6$[ð߆­Íi[Ã$3˜˜]‘±õ–1×u¤ÓàÖÍ¥*®¢¬G\q_J¹¥XãG—Ù~]È&ó(]ÊÔê í™&+ú¹ ÁÇÁT˜$ã”Ù(›|ä\úö8¾ÖsÝæI¦`T¯ÏïýÜ%öÞr3†«çˆêÉ i>-]I ´\Ùu^P‚æÃVMÑÝŠ…áõa[õŒ5g@’2 /}÷dñS@× tŽš›²û”ð îW#!Ɔ¼èߤîçÑá~Ëù¥%2ÿì»]ÚUš kºoðã aô¹?G¯º™b%zXˆw:ž8¡N‹ *]Œ^„ÏÊ}y¼ –ol”]>¯ñj]–Ó3‘øfBy;™÷q¬W/% 'w•±t-ëcÁÛUKgAì[}ý1hõi±£ ôŒí5¸Éä¢ >p>Æaæ¾7ô‰\zîAœA@0$:u«ÏŒ§G4†æA_÷Z1ìÄ4¶q-s®1‘fƒ——eÛ†©ëé@"wü¿¥˜ÙF|°úvº)9*@jÙ§Ñ4ÏÀÕÞý[F¸9 ­Ð/R÷½Áê &µ¾þ.ëÝ„V(ÿ>Õ7â$²ÈOA@Òûí–ºëS~––ÝÌò#úóå·èVÒ}¹ÐÎôãZx¤’ùPÓ¼>úÆ‘­0M9}zgØ?é šåÝÛŽ|ÁQñe¡J"væÅd÷Ú„·êaìN¥ž­â¯[cË3N~Úÿ\Ÿïbœ|;ìr…r#‘®°µÅðšJÔr“ jàá¡þSÉ$Ó=šÒœ(}›ÕC's¶YÔ—Ôö‰d|s;ƒQù©2»N´>ºt!ºªÇÚ@¼Z̳½M<ñRäòîÎIåm¯ïÄ!cÃ:aæ"'FW¼.Yþ¶‡-}tŸêq ©“úòMt¼TDÉÖ׫>?T\¢Ÿåx•·ð & S³F·Rö"cä,±R%D_]’¦nž`Eó‹6µPÀwvË^fFÈVzsvÚ4ƒ[\´… ÀZg­Ä†HX€!äW¡ˆ=i¥ÿ-bÐ̰‰¸iË—0Òmî° ~ëîZDD¬­8NṲ̂µ˜¿±L¹Öö`Z`E(‡@qX“~<{n ÿGâû¥Sÿxßòg£pä`¤ÃC^š…É Áù%„TN¨g?`NÅ ÿñeZ£;“[Ý)Ð7¿$¸(Õ+¬2œx¨’’縕t¢ så°ÚIXÄæ†Ëa—m Ô•Ra€Îm A\c‰ )ÊÊÔi/Bsû6 '^Õvvð+ÓdËÌZò'4&çËaçŠ):HËøAsý76u+ôw<ÄLff²(“àÉݰž‡ Ù™t¤Ù%>l3n½ÊAЭ¨âé{äÎ=RÖø’ÁM"ƒ^g†Õ±ÆšQ–Ê5ˆ¢„àÞ»&èܪCã’Q!û€3_40üY¸%]rPªkñÍ*íxHhÚNÒð»´ûîõ’ä»èb»!ÿpo>}Ñ~ìŸê'¹Ìð…%ômÆÿ] ÎÀurŠŽ<§…Mé.IVðd*%Ë~è®LïûÃ å •å,È ‰L3!:|BFQ>ãŽTöÿK½(]òv»'´6{5áÉ‘,Ë%Z’Ó¹‚ ÃÀä­ÓùןÃײzÛa€€3 Ñw>›Kmó K0ò’ó~Ê$ù²šú|Ø“e#>1l“u­‹B.]Ââ e^„µ¸QÔÁzúH ïÔ£±®•ŠXº'íF=ì¿Uü³V¹ÅÌÕyW™ë,×ýñÿX~p?(NtÌRÏpp[Nb¡œ µ<ðó U @UjžàêC¯æ-O¯œ"çŽFX›wtf­˜f[tÅåñÀXÇ3 =„².ÏÂi« =|lï„ôaqHü|´Ñˆ®¼•—B¨>FQá ÓBžÑçˆohˆ ‹û£)ƒ‘*•ÅÝ[ò’œßIýúƒnˆ¾ G4«žd ×ñµÕËÛ’DT5±HEÙ7óØ¥i7!¾ð•NLL½Eù¶ÆëŒð»4%ûjzÁGEEr‘6ÛÎ)emœ™ä[Ä —L°ØmÀVñíÐI.ª Õæœ’ÉmZ" z—Û…šŠ€ä—˜æõ`‘Ù } Ð¾p¨öBjÎ.¸LE™¬´:+Ñ kIr‡–çkô#%L¾Ê•QÐr¼Í <3IdæH^ùõ¹ö ‡±áBàOps$ÞpËÑNzdޏÐò´cò£åD¿Ž^´ý˜¹^Zó_:kÖ6Ö~ò™í™eO{¥š[ªË‘ÿcT)!ËC:!Ñ mo4è[ðA1£YNó@%œA"“™ZöÇlmv!† Eg FâïœNtWÉè©k`11ªÈ5¤e}Ý1€âÁ º¦`”Œ—tPçe´GAßñ4ž5¸Õf¬¾vF±™¹Só|&ÄíOV8Yv—[RÒBåÕ[$½B†QN;úñÒóÒÿIs€›÷²F:Xã"óÖ_˦dQûÙµñnEmã.vgðhy2ŸšJ™žv1ZÆ)rô80Gé})Þ?ïØ8NŠ%Ó¤¾ŽþÞ(|ì@£·„­×Ͷ,Dy>OÀ#bO÷£ÿh‘Êf`{©ú¨¦Ë&Vñ¤£À‘OLìOù²ìGN@œ"¦©4S¿ ŸO÷1°°t²º‡åÃYcMø&XìÁéDY1ææwU¬÷ŲÀ=¬^!çL°Zp‘z.™ÖðÇ®ÛXMPAé%çú%´/¿´CÎ kt‹í¸;‰üz¡ôÑ®ÉIÛoºu§áa™÷®‡ý¢ÑGÌÚ!w¢Ž»>ÊÎöÒÊO¬´ïêd :’: î[aáRÁ¥ùº„ú¥ƒ¤£¥h÷T¾ Ial/ádÇç8ên´ãÉw•<”KÕ’0F•…€÷eöÀúŒsgJ2Oá¢Õ»‚Á¤e\Ùœk`fgŸÏROÛpǨ﫣¾M³Çæ!M5Î)‘Òé@£Ž¥Ë ± 31¹ß+tD¯b&e KS¹zÖ˜ò—ø¬²¿.-ªL +Žõh­J¾.õ›eÛàšY-¸°‡¶mEoÿ R‰þ“6fXØ0ÀÉŽÀì+8Ò‘6½¥¥WÙá+Dãzþs´\m¯}ÅÍ7Ó*ƒ§,â‘ÓH37ÕÚû?K¦¸ Ì»CQo·‹©;|šFî깥πÚs¥Á¤\ê'r’ôC² 0fô¹37ä6–ðÏ+ß"°º{ù)U-1Ö«¨%ÇH~€"²GÀVâyxßåcÌ;tSå~þÑÎ+þ¥(¨öÖ­m&™IÛ‹gÍv®mט©{·!ÙÉúÝFS ]ˆî–ãŽ>æà~ýÈÀ´/4En©sèw­K¾ÕkJ)Xòzç‚÷ÿ`‘eE–2'à_CDpñÍF¤§ÃÆe<º¿Â²Î•"éårï-ÌŸ$Aœd÷8 ¨ ïzÚi@F¢ùý‰8àDoÝa{¯Ï&Þ¬Úø+˜‚j'¿ÖjRW£²9„^iÀ2¨>úhÑrQãýõQ²dêÁƒQbpd½ ¯ÚHñrãXÅKt°˜#ÈêGý ° ž‰UäXbû:Ó—å¯0£'–Uí¦åx&6®×Ÿ’œ¼ðùo(Ö6 ì-v}àj›DK«“v†T—E*âp! <¢ÄE-OÿNÐöݽkvJú†÷÷k¿ß:/,ä_ºi86ÉÏtûð² ¯T» ñÁ@>¼]H,t*F¶€µµÏmƒœŸ6#øyˆl’VÈ|‚Eøý‡îúAËq¥¸Œ”zºæ[P=ë_-)0fX)`ÞM!b¯ô~OjÈbY~Ü^àHB~ùì|ª™#AÞâJÖxE=ß$×É®ñ»Œ&îs)k¡†Fò2E0$Î}Ýp¬1·Ø×JÃÉw«† µ%Eeo)®{ÀÍ—Mƒ½–•Ú}Ý"“ŒÀyæ›Y‡ w•ëÜ[âˆQf2èéõ[.Àó*-†> ¨6.ÇœÀRf+pOšP[|¯Ükʃ'iîéM¸až¹ˆýÎò9}žºúÀ¹ŸßGHNt“º›ˆn&…kLjºTQ-5É•7¼gÜžzº0«Æ™1ï½w„bíÊ“`ô¾œY¹Ø½‹· Ôö—‰"f°{Ër%ÄúàHpÓ:yÚº6à·KfÖš£ RkLàEí5OðÛvþÐÙb(K8f.î™…Qd ö kðßvý~2¦`x<—íÜÈ·Ü/Ãm…Dæª$2 Á-¤Ç“L6«RêX ¢få\þÓ¸‡ï펻¬6ë`ßàé§@ìç;ú84ŠÀ5«CøY¼ÝO{ľ‘t&_áÂ5Br°\à¼ð,±×ÔIÞ¾}hÙU>6R=)››H´z`°2®2`urd©+Yú<5vÈ·bõÿL¬‘Keöýl §»”ÒÊ7»9ÓOñÚ x@.Û÷‚ˆW.ÜÌ7Ñ’µ ­øÏ}Sx ›‰ N‹}óWö‰B(/ùªÅ^ßUÔM¬$üu™öÑÒø)gL‡g”¬s·÷±M RM…5h®Æî´Þ`åw~xŸý'“¾Aš§n‚TØrôè *ÇKï÷ÙŽº@’3¤ê¥þt(€ª9/[®Æ£/ÂeqîáÒ‡]B#5«-åØÇfU¡ÿŸV< óÑY6wª“!ØìSÉæ^dyÝ‘šW]8Q¡´Ø~òÏ,Ÿ;5ï ÛèVÄo¶ès wG Â…‹ÉK Œ“n5òy01êŒ{ž ÿEáî<ÛµTêÝçÛ–þØÒÈòëì^ªµ¢€YŠoW¾ËœcYÿmÒÐXמ£%ù“R{Q\ah#[Ô‚­ÔâOkuȯêUê„§ÿ:HeÞשÍÛnÅž³žkƒj;¢ À ºÌ¯þhŽ|"—Vƒ¥q{\€{îJ\ˆÖ\ cY§ÐàÎÂp×aD÷PðA!¦À$lný™¨¤¼R¨Áº»b.¹e.K>5T @ÊAûÑy¥@½²A»}ÿzL@œÆŒA&¨Å¯š®fOÍ8ÛeMGš®b)QÔl¾ì`ÞW©ˆß¶¶ý?¼{îD¯b[tüUxr×ÎŽƒÉ›:H¯‹=]¥Tßž®9î¨ížþm@Z®]Fp‹(ªÿs!‰l$w1ª‡'Þ±­yµÌÂ…×Z.5œ Øo6õmΨ†TýªÊì=t£wŠäÚß½.² ?&Nvª\S‹rÔöÔc¡rzßäBBF µý&q1¦>Ã'9a° õIåmï+‰œóÛðë—èräoùB]о7}ÇÃÚò'QgpþE@$ßÒ ´¾ã~­°+&@‰ý&y梿  ­š‰Ù?ÞÎÎ’¼*Ù5òûP-~áð¾$ðÞ™´\’o ‰ƒ©üÌ‘}örѪ»ÄÿÊ$e?¢Õœ8þÏj¬r¬.Eš(Ý¢Œ6—FHØ£±×äkUßOÝÕÚT/ß»…J¹=ÏíB»K…Êa(>ɃÒuuUzì=ÎÓê@éÑeùÏl»ü€5˜[ûöFOêéµ¶½:Ð Áuça‘δi. žî !’®çüFV viO5×R«>•$û\³Bɰ*èyò$iè4K:–ïöÂ%çºÎ}¦ÜÄÎzøÑXg¥*Õ,k§f‹H q :–öù®C×ðØÉíTK—¦Ü°ƒõ6·Ýó¦S| ­ÅGÅŠ[d¤¸Òte¸ÏØ-!²¦®8ÿƒê(NÝ Ô£·êµT•ÉH®*pá¿Ò".¦û\\çXðŸòëJ~J ”1°”`®@ Þ?W)½Ð;€g,jèzÃAì6ˆC\møÚõ£‚‚‚õìô€'RÝç5†8#zÂÄäìê3ÏMU`I’!ÏÍÇŠ2e½TçB…‡¹öë%Šjõè|dš’ŸZ>§ääÏo^{ ÒhÌ”õÓ¡N0Vj˜—Ê« øqE -Y3m«›¨p€˜dÇ‹®>€¢˜®ÅÐÁ&iŽgÒŒ-æšcC“÷¶ÖXùr+0A­‰Õ˜FÂŒG]O\{ùû)xRÍ~M/ÿ‘jž•`Ò×”ÒKXA{b¶Aí»‰¯¯p¹Þ~í‘ÞK>ÌÄ’œÍoÞN½7#r5Ìê͸:ßÙDOÊ‘ÊÂK?Œ¿bѰƒ&vnSéçh0zVˆ/Å'ri¼^äÙÈ K¶ä`M,ˆU‘»jþMªVfQÛa‚ñ#׿Œ|¨Õ7Ìf©ÄXxÊÏÉ%YÓCÇd ‘\ºL´š¬'ä••Œ¹óME i72m2Ð'ÎÏNx"÷0@Ô¶0ðhvNCw#ôÅ‘õnñh8˜µÐV#ñ¶Ù…íÜuçûÜIû±½f^}¥_oŒÆ¯qÛGÀt­§; ± óÍ9¬|$òØÐ….x_²ux¦ÎŒO†œ1Qí "®Àœ¢ »Áv€¥¿u)Ã,CõHÉ×kæIzQ܃å’hZ¿|!,•ZPY4hψ7Ø(ÂäMщMp×XÞ?kg"ò©a¤ëµäóñ0žq©¥Ì ã‰iÇËú:‰dw&µu‘"wó‚>†)éÕ?+—Eý6ûß©a‡ÏW¬ÇÉðSM¾Ýf÷{ƒNŽ!"‡—“ÝvAƒó~®ˆ–?«kCÃͺià 9ôM••b;$ÅE!óCí %ŸllªzB[VÃÌÈÅäå³¥w°9˜(ÐmìÆ+ïëŒÍ °êO¨ðgÀ¿Ü]Žèv¯ì'ÿMôB!‚ÍB˜ìj];Ï]ìÔ0üùH(-“ê—læ3„'Ë?Þ.­­PálÖi¹­Mj/y3U1h-éo†’ÏihšÜL¨¯|NtûºlB[ÈB@Œ&à‡v­îÁýÝ ê}³f½cM‹ÀJx}.ÿpšà7ýÃNL¤Ò)B‰,£o¥t9ˆ°Ô»1žÌÖÐi/ÕM¢&"´ß}Ž˜ˆT)ïkJÎPOŒFh•3|-oX=-z…ƒÑÙu¤e8î»æÝa*‰ôÒ0õø;ÔÈŸw¿pà]7Æ!~T‰·(ûV/ô_|sJ€á­!Ÿ›’bíB/!ýK–"mBrd³½U®@·õŽ1´­E|b %„ýÅÜ NpdU5ÑÆwAÖ &ÑÛBD‹H‘ŠÌ»å5J?I'¹Ít&4–Ä TÓÖÞ‡îRì—azdAfdÀN J":uÂèBï|(½²ÖÄtAwþ›paà^’§ú5CV¥µ6@<Ì”~7µq‡å ’ Výµ;¯*WbgšH&ÌCj\2šT_©‹ì—óÚ±ž„ŽWØ-︦Î]Pu?žsý=·–„ïô¢äDƒ=”ê¯Ý‘ƒ¼ñóù:´_‚ÝÏki:¡µ…ÊûTĆo "eÅvÖKG|Õ¤J¤ #rú™nš¾ ×eC9v¾+Z&@R/úi¼œ{n!îÑ^>¬‰«kÍ}”° N1téõôrx]EmõÚ‰†Ùù §7…þäá;–Za_(AÅ“~{K‚ {‹±{æw¶Ýt‹{¤­æ^Òi·q¾?׫Ui„{î"Všn~jSê';h ðáOo*Ý6uoÜOõä–ambûžÿG'<êJ@Y='|,š7 ðOôöKà‚!÷-ÁÁ$˜O ~M¨q ¥Zû˜ï“* ÐÁ3 yú†˜TëêG'ŒO®+Ìš%°e&(VÆνzß—÷U?›61~©ú®ÕÈ‘]¼…ÄøYU™Üï¢–Ž—^i½d•êÏ@Ó…“.7Ç‘»{] -Ã#'¥ÊŽgÌ$± ú P‹oùåûˉüFƒÅxÝ Ryš¼BôÚÝÿ~¼Ï‹gn¸z¾¯ŽüfDš§©xüˆºÉÈ%Mó¶‰uS¬?äâ¡gžã¾aeþýâwB(äÈ_Þö‡gµGIrkBA‘0+÷„î;€ypà#¥tòÔ‚»k‰$CèˆöOfôC}•×u’ñÙ¾*SEb³[Öˆ[äÆêù0ˆ‡^!ïÁè\ ekÌ+iõurŒ Öêq”`Âeëó“’φ @y]’b†D¡gXMõÿðF \LñÁD.ZR>ÊLÝåh ðÉÝc Ï{5£}Ià•šÛ`ö4we”í¢³ì>n=ˆždN1SÜx»ËÔ)ec`MîÏ.;¸Nöj®€Ì1S‹€„¼]öüQsŠï'ó•‡!©r‡E$Î<Öf½ù½Ò ‘[«£ àü$ã»5 ‡ÜÇ‹ôOø}?ýÓôÙ÷ J³™Än:)n–N#½]Í<à®Ï†ê(‘ïæÇäjçMõòç¸Fj@3öúŒtîmÇZÏææQP–´àÛnïÿlÛïO=Ùrù4V^¼à>UT2j‚êÅa”w7¦L‚M¬ùˆ ñÒÆØ e‚ŸÁ QèwàÀCó1ä5ÈN¨¼îKÜÌLk¨È'XÄà ±`ŒÃÝù{[ØHVˆi&Û¶!9«UR9Ø„ kêÊ¿éÿfg3­_ G‚¡Þûþ¡ P¯+Ô»™ÁÃÒªMðx¬“Ÿ/æ­¨‚ÄÌ$š•’dXM¤Ý¢âg"´Äæ³¶R¥\û®=ÝÐóGc§U`d¨7$o¦t-.Ŗ딽 E~›ûGÂq+'9yÂZÙ“ÄüC±¡BúMBz˜iÚîGÀÕ¨¤±‡zýN)/´j}Æ^xqQ#Fa°#Ž´¡þ·Ì‡å¨Z¹÷ó”ê0ZôôX•B Ëë½]’ãrJ‰‘O÷‹tÖR0ÞžLéíE` }‚cªÆ3w–mJ 4”^oÒ»9ÅlÐXbf -ýÌR±j•â¨!è9ã•»pØ‚Ùê38·ÖE–~×  NA¬*Ôù;ÑnHSëL%ç$n éâ™#5X틯O[ KR6Üg±Â\ùóE¥ëñX·mµ‚YL¨ÃÇ þµ¦H߉åØ:¿lèð•ŠSÁ©XÁâ:œ*Hda÷ò”ÛÒ“ƒó]÷KŸ2•Àótac¤¨ÈiQ«Û¤|,Ì"·;-¹`]Ô,(z <¹c§¿W@ÓŽcZ‚wß—Lý;ê ` š¯”µæû‚Óº5®“bd°.¾ÔB£Ð'öj [¼)QÕ¸Ìsæ€HpòZ5~oÆÆÉ‡ˆú¾åFµÕðÔGU[_°Ë%µÑvx£˜¢|…êß<ž©%íÂnö"ÊLx®0®öÛÌÛ$ Ë*kŒ§˜ÊL=+M³Œü’8 %{R‘ÁÇb/i(N5>aéÇE£edçª>>°•í! 3.' àðtF;\ZÎÓ©«Î$”}ñ†¨g~BÊh(”†imáèÖŸ)S¥,p[[ nÜWŽÀÞUâÀ&¼¢9¹œD mãk28õØ{º ŠÇd'¤vØ6¨žŒx$;~(‚ËZ͆—b Æ޵<âwüóvµH )O—ìûÛٟΫ"íàÞµPÙizMÁà‰ããʇ¿Ïk¯›/,ÒÁ|Ä.èt†Ÿ™¾³ó¤´-sß =˜Íh¥ðAÈ‚‘îªÝÁ±ª/§‹¤=ˆ È9+#i·NâÜ=ŠµÌ•Oì$ćl„›—JçÏgÇ9°ûC[XŸBõ’EjÐTC÷#w vò÷%àõ2y$­Oçê²bØ%IÃMjºVZÕ?y£6¥Ÿlaýé.Iøx£¸ ¢lßÅ܉ªE—%œòH[½pÌédñ1{‹¯‘_’Š4¹ãiX–GÇP3§\¢¹Ûþ×7,wîÔd}>|´âT¥u?§®'F/(xÖ¥Š'¡ãò¥ÙÉÀð‰ª°P ‰"ùYè:“J³ô× E¤fL†«ù«U[³ÌÉŠf1T fé¢ãznÒ-ÀIŸSöU±›Œ’E‹Ð2˜V ¼Kõ•fÖ•¨$yƒ³Ò0O?åÍ: =÷k\}h2ò©šLÔ¸åp×´ç¨àöºË<Ü›™Ä&ðG€\ã#2' ÐêZ §&c2ÝI‰=3r¼þ ª+J@T‹³5;òZ]u´QžãxX”›é—pë!×ñhø iûÕŒs„è_s>¯’7Á|¤tgS¬Ýú»:ÁZ¤»ÇñG5ÀÔUf#±g[)}×ܵ™tÆ;{n$¢bŒ0›gåg{ÏF6.7Ü5×LI>“\_I>¯5Šó×AØÔ$Šq@ ®vÌ Ò”ç€ôŸJ*¡ñ¿ÕuïeúŠVˆìaí¶êèP3R~›3u&½9Íèöç´j›æÔØ¡\ˆ–: )/Ïœù&ËŽ]LÔ#ŒLÃ(Gøù¹*‹~áŸjq&²2³ËÏ?á:I÷Ë † ;RÖv¬˜«êFyÜXÅ• ÿ5bp¾µš«2ò)µ«è§ö¨5êø¦M[=R´4ÃçD°½ 5šÍš}øsYÀß'鬗z:a.òÙRrå±Å±t£Ð¸ÀàfALI×´øm•üßý´‚êªÍXñÐÙ@ #î¼¥ÀÞŽš+KŸ P1§ÎhFYNDΫFÆbR9ÿÕNƒ YLgé~y^ôî3DM‹PóPaF›Ê ,<‡Êç0à;±Rd‘€ø*΃ð!–Íä\Bóî¹}#èz·¦Ÿñò÷â<úËí €VÜ*ýø†Å°Ì?â5p €"ÖŽ0kú ØÈfô»±ÂziM`k9ÒÒÛ dË÷²çͪÈöÿ[]b!j+2l}—<{^6 µù[/4´ˆÐ*˜ï~0‘Åýá´õâÂ'ÂlŠ6ꈎOzSØøjô°“×wæ2fxÎXÈÂs±é±SÀƒ ÅnèP3ÏouaØO.x>+¬ÅÝvvyhÃÂSLñ0õ|ñî¸Sk eþEváÖ#£€ìÂ×B5³€ÇÌ›0æ?º/ÌWƒ—9—Èa_ñ)L^!óRE$/†i¹ÒýÌ!Ó;µô}f”(Öhèž²Iû³ðhï»_™w[' Êâ‹; \ÆÀ®‡pœŠÒD{8{VÑø÷ÈjoE˜'¾ºµ´Ó9àŠŠßßN`/™ÔYLyîeÚÏD„œ'XH2,°ê3ö÷âøè,D—Ìúpz2â& Buºc÷rqQGi6Û•âƒ_ºï_1 ͥݮ  Ém/H2Á(|€,›îe7Ý¢oGÁ¡N)œ/P¾õZsá¤/ù¤¦Ñ‹Ør]cäo+¨»Ÿž5îJãÚkÑ'€/À>·àe1\‘õ§÷ì*Èl¦kþcOLˆ.äG¥Z“ãAúÆ6Ú,~z’Ž—æ6üÌ{+ÿ€OaæÃrâjqôÿ9‘(E"*dCa¨üã7›Æ;Iò„Ò‡ª¿ <Þ(°ùòä¬ÛuDâgeYR²Þé’ï”Ú£ ö´ÂXÚw[Vúóƃ43›{z ÌÛ×®ü8m|ëóæ› iÚT™Tw>ó’CHoÝ_äPh¸õÆjöŽÐî++ÈÇ  [½Õ¯5®vút?p–¬ÓI,†=~C·ä,7Á:,mK»[éíR‰XŸÀ ýŒÈVáÄ©!) ’ñ.&C.$!Z€×¦Û­/ìﯽñÓÐc×ï v¦qnº€›ÒÿR+dû±3;ìÉ}åN‰ã!•S5ÂÝûúõ—©9c;cbÒ×o°ùx¬²¤‚—6ðPЫ.&;»†J½Õ’ï*¬of¦¥Ñ=ùò‡~׌|¤,àŠ#%ÖÖ" )ã1Ü$~¬˜Mþ ÞüHHæø^ú'KüýüŒ¼ÃÜœ¸L|£èÁÈëÚ‘5ôûYd;,«É(%œ¡7‡1]l–Ë݈˜^¤\yDPqP_¶À<+ç3¢J•¬pPGYôѼ¨¢·×62'ÉóðⳑíÉ ƒ¤:böË©M>èO:PÏ›¿Ò]¾¢{RãÂh ~D‚(üÈ$‚‚À>S8j&Ì‚3F›o»ÀeÓ†_¨ídjI|·ôÙt‹®Ð«ÎlpN'ÝpFý!õô1_$Ž½ßŸEÏöÁÖÔáuamÌ…<Ùü!3>kK vÉ0')FðŽÞYC¹è+3[£ì!Ìt¹(Š4Í®vçReO'è’‹µg±c½ç-,ÔΦì-w0$ð<•ºRáǺ™Ü•1­ HœJ’hF§ýaö…•tJ:·õZÚiÝêúc°PR2Éüæ`&¶ |°B»’¹Š;!nlrŸvl4©´Ç&’Þ¨°=­&þq;8Ñ€4ç¸'9ð3ö²ÄÆ“b)õúzŒ<–L‹àÆaÅT“éûºP†¨í~Krj ^”ïP‰™¿Ìlâ»øk g)…«N™€Á)È91ï“£R6$® rŽt ‹·ë.iRë PèìÒZŒÅމpÛü"ºîRœm¤BóRÎQ·UTq`ÏsÒ!aYoøÅuž¶~᯺‘çnfþ؉’á©S»—ý!¢Çï”tfL¼ ÿ%ÿ>âøyèœßÑ µèÊÇ­±þnN]a#Õ‰Ú³C…XW‹m»*r½¡¯Æ2°1û†Ï”_z(˜/¤LW”û&ïm_C2ý-žS qHp“ŠÔdÝhµÝ\t”@ T:ï™I‰È:ÒácgJQžoVMÍ«†už™^o!õÖÂLc^ðÛ?Ï`]¬štü<yvë®·59X”°1÷†TØþeßþWÙ$‡íKZÒËknc-'…¿C'–¹I˜,ñ<3CîX+6»±JÆLz´§×ƒ h¼äÆ)áøþ)è'-€©œç=Ø¡JJ‘è¸Fó}*X¿ÿwcCqPP*ƒ;¬[žMµ&ÅÝûÕÌË’¨Û_¼Õ¢Lcd¤Á î`=L¢¼9æBal¿â ¾~¤Z‡‚$ øıܿÌ+X•HÍ–†Pû8½n„¯0ß|­@uÚ³£@[ÙcÈí•j¨ó=Ãi¹¡__7è{”Ú^\+²F–g--b±²Žx U7„„`¨Æä˜à¯ÝPÈGz—þvÀî¶åЂI൒býf”4ÙKðHWýŸØÌîk‰õ2‘¥zü>U£qFi w~ jšÑ‰©;ûìixÕ×ÅK|³S4uåNu"/7þ›f€_+hD >UßVª½ò±Ž6¼Úûƒ~@/%:­õ›Ë¶hÝdÏêæÒÑ”¡*Òb<4ÌcÜ™þhøÇÆ ‚§„ý÷al'ÊÞºŒ_X×0ƒ¨3û“-dl­³¢{Š*gÜAÞèSÜÎqe‡Ì†c™¾‚Ø&¥@GKÝTݦ’Ïb"e¬yuU[soù.„zç†$«ï÷J„ŸºÀs´%`.ìo§\Üpû,SHË… ¿çåöÞÎMÃÔ¥¦;{‚Ø“‹ê×ò`cÒÙ½|"6¤SVq‚ >—.}U©$m@¬o5ß‹™×…{j̃r†ÒÕÌA‰ôÞÆ6‰ƒËäMhA#“È×ï/´ÝÈbði––aÁ]{¢NEïLëê#>ï¬ý·Ñ÷©CÒ4á®§Õ¯‡7 Wéfµ$㺔TØB!œkç8å›EâÖM#ý’l·•MíÌÄ¿/Q[ÇÿDP³o+Ág&ª±ãC3ÆêÏ?#†‚'–‡ß²‚Õjxꇤ"6Rn³OaçsCˆŸØç4"‘Ó CmA&9wwËÖÅ/°” <‰@–µ¦3u#ÄÔyÌ.Wõšãã¾Ïö-fyhAø =²€¢Îñ•c©êã0h@ÂÙ›B«tŠÿDÊ~sÜú•ãä)™ÿàNé-“³4ásÿsÚá* °_*›Æ1UïA$…˜êõ[âÉoi ”9y‡‰å®q¯hÍ­#Žæ—c®Ð`„‘[ÁFŽ9|¹iJžêà«ïûñ1RËuļ3D¸w$IV #œ²kMMb»óËß,iedY—ŸcSÖ GŒÙ’~г-fÑ7IòÝp²ãÞéÿ3‘›¡8¿¯èÔ ³—9Þ.:ðÈw ÿi™Š†~„̲¯‡ÑÍ5²+¿Ø¯ßýðs<ú™~Š´²})/~–`ŽONž§’wœ¿$Éç¢MÀß´Ä¿8ðY_aÈ n§Ú„M–Fh‘CC:5×#5@ÀÑF½<ê®Ç ¸ºdCrÛ;ˆú Žãœ‹%ªo’Úh‰Û—ëFµÓ´sX½`jn²*WðÔÔ°Ñ"d„*+Tʵ‚3-ŸAý]1‰|÷›²Þ^ný¶~OàcU^N©.uùÜzÂö hÝ4}‹É…JPDÎ3Ä g =âh%œó„ Ï‹ÿ4G¶€!Á‰ïÂÞôqÑ‚ˆ¦¾e»J .3ìäÿU‹Œ3 ô \,wN ÏC~ºËuôî{fÞz“ú²ä‚ëÐsh¥H åX‡æ”¥|ý\ÁÒãQšàêuÊB4_žÑ!UI”i›òÛ±fÄ÷Ž"«–P.5< ¥6`ŸYíq²‘'”ž€³0ØÙplç s ì?Ùxd¿fa]kËYÈd€¾Ït"'uJbRúÖ Ÿ»9Væ¿M•¨®Å ?$;dOÁÀ bÂe,GkYìa—‡ù0_¡ÿ>}.Ÿ÷7óh¶Í«[ÇŽTtjz™\6Y]_iq‰ÆåÙ<0|¦-[ÅN¿"+("3lýëshöÓ¾ÿKj5ÜG—°Bû†Cøfß~~¥Ëh¥Ì¡¡ÝœêŒ+õæhŽÍÕ' m&YSFœ­ÿŠS’ýwKÐùc˜FBëEK¬*â½l´fÁU¾Q‚:4%–øÔ_¡^`OñÃÞ &Ù¨âüÄz¦b™¼µ§x;¦TUÃOb£]J¦¹äÊ)Ò:±_†jnÅLëG:äa•Hf(2/:Ã#զȺ/2Ûlž ȯñ¤gÙøüG@!tlV0¤°S‘´W†Q¿”ÐCóINvî!¬µ«W-C¢HÇŒdJ*zê³øIB½BÎíO®…¡Aÿ-ײY†>úͯ_ëø]ŠH6¨ P€¢”’Û<󚊾šb4ÜM;Š@(ÔoXš¸Òµ|5TÛx,¤Œë\€xb`ÓïŽ8O÷äøÚÓô!$®ÕQ%E•]^ípF`X†s¼,Õù¾R†,Þcœ=‘÷·€ÿÞ´V3¶NÚÅ×êá;x*^Z$’ØÍ¸=5ŠŠ*”—ü]ECbGîCw:3&F0¤Þ¥Ö,0bp³ŽB‘iAFôvŒ å ²Ô¼9º•ÓÖ„°5|b¡s4#íoQk&Æ&x¬áGnöî‘ïø0ÚJÒÏÛÿ ó³¶ ßÛA{@òp}÷bs¶|d‘øã¦I/¶…™ä y'¹S>AeÛòe!ÊõÈ,F-%á >]> vH×À¾ ¤9m cu0vƦ {\Áq‘´¤ˆPPR)ÞiX æÒmR3ц{þùÚ·uϳ¾œDj1îÕ+Èç+ëL¯S€¤{=geCÚ{7ƒEI‚G«ˆ&ÿgÖ8.ÀO6±¨p~ÑògËãt°sþïF•Áy—õMBCWÍby#bexPqWôäoIÅR=âüUMcà5/Ì’R1ÿUÊrwà•úÕ_õì¤sny·Yä̇w>”VK&–ÇþVB#>Œw¡â^’Å!±º°«ÉLî9Î7Êxp×ZÁKyŠ|&É:P¬y j™VÃ( ëá æXö‹dQ^5V,ôÜeÐ1í™>ͺýò6¹Ä“¸æ,Ôpô0ê,#~=ËäMuþânä§H¼ßÇ×.Þ \Ťø€ãúfALpÐ (öûljòƒ{Ñ«¡Þ5@–°çu¯mHŒ“°\ù±M¯À ­FæAi.ÕöÔ ô'„öéR"Jè3…N“$,i:>”CNw…J9›QåÓ³ÑÆŽ²pŒˆR€ƒîà@ùxF|$;ᵘ!Æu¨O@³¼÷†Ùð݉Ínr×bâu'r2$/ü¹—þ\Œ¯…îϬfQQ`6 O°¥{§xBF¢HÌÍe,Ýõ7ÏÈb4ªãŒ*.½¿üh}çÇí.Ì-<óµ}ªS¿Ú¡®Š#?Q¦ åòªOl$Èòýjã1*rxž_×=¤^˜ ãA®Àäø#˜jÙê…ô¥rˆ)úM1H㸠òî=J¡±šD\ÙcóÔ–£¥ÐGKàB8K„A²0 ½@;öØâ‚±JŽí­fb,VmMÞ/>×{¶¿[ø¬÷¨¶ †\C+â^ò-Æä‚ƒ6EÙ`{ɲTª±ËÄÊêùSÞζ;Rl‚#×)¬¨©ÓŸ3=ÂiCºýÈ Q²¤${ØÁd’ÀÂyst£_{¸Ðí¡*¬dÌêžöJò !ÜJëPÏà3÷l5_ºˆuGÜ" îœ_’Tœ-¢ 4ÔºÝXï÷å³Rr¶`ã3¼ ÿzï‡gr!k–mÝJ}+˜ä¦u{y÷ó!=¡…w[hleøÍ,3©*&МãM¥ä©—´ØRc¡*œƒK$¿âId¥à͈ȴ9\åïæ3æc²á™?ŽËÿP³È ì âeæþw<ƒ£]ãmþ¿Ù%rW¸îÇA2^ø€Ñ+ä e;¬#Âo ö¾\8)¶ˆwëñéA¨Ö O›-9‘ÊE¼uA)"… Y6»3ྒ'BÃüÇÃcGéîš8ñB»WìtFšÅy‰‘„˜W<ô(3ŽügôÇ|ë”b³n£˜B¤«´Ë¥}¨gÏ9èKK)<4¸Àár™îr´î9šm2y~;æÂç€=þ£¬t%#•úviíp¹½XÐN]x”Xë”)8ý¼Ø°‰Ò"×í¥?×WÝ.þ”ºŒ±U†Ï“%UVé;ER ûªÚrÚ^b¨ê›ÄPã&}×ç7g?vÖ‚>}ËdÚ2—ø 8,m>½á.}m]Üйª:«¸=BY.¶†ËúùÏŠ–ý“p?þ“±"f§K-(‘‰ÙO·OZ÷= 5"¸œª¢ã7Ù.u·m ˆ¡®ù%ÿjž£C6¦òMœ‰ !#v1ÓÅÛF"‰}d|ågä$‚À*Å*¾¹'ŒЖªj Ø—ÿûø¥¼.OðÄØÕ Öä°:ƒnÅë|ßd¹ÖÝ´&"†»ä—ÿ*z |Ú›É4Nr&€„ØÇOmŠ%ôYa‚ò³òûVïRÀ«š §Gà kúœ†*SÉÿü²Qô°³cM–ù¿­ÓˆÕã&¦¨,•'‘mbfl=¤š'9@BFìc§‹¶ŒEú¹°AyYù ì˜X¤ÎîxÞ»¤NÆÃ3(¬&i4ùËXB(èÉÐ`d˜¨YÂk7ãeuà‘ÙxhÅ™µ; €¢ú—Ë- z ¯ìÀCµŠgðïóà;9C3É—1åúŒÅ½µ ò>[²"}D#…¼ƒ¶™³[æûUï “ähI‡[g8>ðzï*°óÉ4Nr*p{ÒÈw¡qvш¢_A%v™?{$À|hŠiÖ#¦¾ò_íw;r)ÅÉj?9*Yt/á [Àaé˜âòîÒsŒ&§ä&¢,ÿQ’ù5¥FQË‘¯·zžø8Ð ªÂoSâiòX5À6%–5­(Me¯³²l=L úŒ©õ|˜mù^¬Ñ'AÂä^ɳï&P'¥²ÿF¼®Jù,.¦«¬ú°çV»BµL(ŸÅ¦Cn$/Uå½ðsÒ4¸$@<¶¨FÐjú³äZÚ5¼Q3pO´â'¹´ž‹±VèóR~+ƒ×x½¹(h˰JÁ«Œ*)²¦ËÛˆ²ÿw?°òßþ6]ï-~´¨´Ä¼(ƒë8è:Ƴ+áïcMÿvH˜Î(ìd¿ù˜É2»Þ:i§+Õ=ÌÃ9r…³Å"+g!±çáì*f”§éO «[†AÃ#œ4áe-&Ÿùa@EæÎåÑI K~„šP”ópªv#³}Î ,õõË´/#ªËœ.V;V†«Âf?K¢K40hö˜^ÊÅFù4ÂÇ\wÖüý;£csÍ9wTú寖µËç”úÝÓ›ØiFÏŽð¹2.!þ…˜_G¯ä)‚„@q#ÄÃ'™Ïû×ËâP-°¸ñu¹[D(Šd¿ŒêÅü-¤fB†ÈÄOaË3A¨ôKéBl#gûoy8j,SÈ×êùvš*.ÄwnøüT Õì‡Î\pÚ°¢v—tP&$NáèvjéG)΄wă¾‰òtÓ $×Ôå“x™ëœÓ»?X݄ҠjAø–^µD9V®o`ç¤ONóï D?¯æiþK‰û¦°_︞Bìi(Ë%\Íæ›œŽiýÂæEÙ¡0 $œ­*‰¬ü’%Õ ˆëèmé`è4ç…®iÔÂ)ë‡ú©ö7—Ýäæ›ñby×vSPJ²¥ËUR¦ì *ø/{œï-ƒ"üŠö \m+6ÎàôÞ˜¿Jyó¾§*ð¬Ú‹,Jt«Mpðjæî~‚a½Çë”®œÈž.+‚殡Tàuð`ŒŒ l#<PXjŒI‡ÀÖ¼Ûð—í¶Zü¼8ÂÁáQpaó)NE‹«ç»â‡Y¹èÜ1Ùñ Æ ž28”[Ó°bØ?…ÕìEÑÿMyì¶T÷ž…%$ŸÈæóhæÔl r=hæ7zwË»*-Ów¶5c<$|j íž ÇÈiÈòËlÓ2.ò›ÄþŒHS]﹟£å„r??êCÃ-qœ7F¬aÁ/þ¤dEÞÝCœkÁöªºú®NÌÁ7Ѓ§ÄLŽÊ⌯¡HÍz9d{*î Üû )CZ¨!Õ—Â#³á¶æW ÿ  B¤aéÿѸ呲Gë&WEÖŠœÀ\$¥‚ƒ£ ´Õ• ÉÚ¨bÆ«íCmnÄFŶðL¡—ÌfP¥ÈžÿBây—`ãX&´ºÑ£c-7S  £» »­àn989I·˜­,ŸQ‘®Gåm\·B±FÁ¤67醢/xQ÷7uì}ÑæE¦´]ýŠÕYKcqUüGRáÝ";3Ã¥L ¡ŠÖ;k/ϸ Ûµ»„b¯‰~Œá3&÷Ñ6Ì ˜kkÜMe1c¹ÜA×AoÆü&¾¦Z×½ýƒØR¢ôgà}rånû߸¢¾à4dÇ”—6‚««i8¶„°(ez˜Ã-3âP“¡1ùZªì°1¸³ÂÃ)ØPDü&TžØû$­ŒI|€ñ8ž­¹YŒ>øÆ<ŸF5Ö²k¾`~P÷DÝXȢϢ,€ì}ЏO/6< n]ÜW¿ïÿ} šºx{ìòÁ áµëágø EÁô¿2öÔ¼l2VÓÓù¦n¤:°m…{&Êb‘7j¡}ë`êg+ÅvÅÑp 6 [³Ô–P‹a"š0Ì#çÃ_ˆÛ¶íYÂóŠÆ<lçÇŸ¹_^y´¬:œ1V®Å…TÂÄ.#ÔSWœªõÖþŸè»YÁî ¢i.g¨ D9Œ^©öÐÞAQàÿÿ|¢q¨åú3ð¹yíq íñß] ¶ψƒJæ<ýÊúóÍ¥aÔኵv,*¦!q¢š¼åW®·ôÿ"ìag#¸2‰¤¹ž 1…ÕqñF{~¶+°ü¥e $q¡øaªÐ¹#GÍD6–Ä?ÿp©N¿ôOmkÄÅƒŽ¶s³„²I^yæÒ°êtpš¦ÉEE± Òë_¼ÐUzëOô]Œ,à„wQ4—3Ô"éc"+~  ÆÔ¿9˜YT‚ôž„˵€ (/ ù"b9“=–kÓ¯»ÙÇ˰ž ¾·×Lü•ò9Ö󠇈\B–à^ÉC»Ž8®(_üHGŸ\¿‰{¹ŠG)Kñ¤ˆ%:WylGäeñ9ŠvËÿºÿ¼ás=à"k91ü›AΙ»ëÁz4½OnWמm+­Š)Z”CŽôðŒ9X)†VÝ€{§} a)÷<Ð'žý.È÷¯sî½d/š¸\{ŽÌ7m¨l-±Pª„hv¡$_›RÅ‹¿õL!õTz¯EA â%­rì½ÕÐ|ßm1Žœî‚”öØ`Ž]™q¤Zñ‡kúTˆµ¡d¢œmíO•`ßÝ4³|K$›-ÈŠà˜€¡±BóH«±1µ_ËçŽñ“õëíåF#M© òš+¢–e‹æÂgúb¥š­ŸN,À݆’x Ú5ÌÓ4Ç¿æ ’ŒÆŽ'OàY#±Ð( *½Ä6•t.kÝeFöÄ¢¢ÑÿSU¼76³—¨<†ŠL‚V5Ç£˜”Ó>†÷ùèôeY,¸´RÕ¥ñÊ=¢¬Ìs³´j¿ÅÍ$¯ðC¬É@|9L d·ø›õ tÔ9Ö”ÉÔZL}¶vXËhl/¬_›ØLáx}gâm)Ùa[ÊÍú¼½LÀçKíñ>½æ¥ã­´=MT4„¾q@õ„Àb @ï©tœž÷QGŠ«»åÚ4¢vGgñ}öt3W;VÛœ©ö2ÐEáÄ´¹•ÊBU¶RiÜÒÀ¥a¸Ý—aïùH¾Sù!Ýã:ÿ6 Ízfïõ÷2“Û.VM¶ùŒˆT„bÆYÒØ/“‘eàÙDø^~­g=Ò$GH~݃)5 z«Bíæ™.Æœkdá¯ÇÍ¡L7HºÏ¢+šÍUÙ@GÖ‚Km"ÚJµàNØ€+št0£I:ù+–Koìäö¥BZ‰«½³‹eÛYLqRpIÞÅj|.Wgݾ”U¾)1&Fá>¯(….gó¤Ï”a¯ý¬úªµAš”j`‚œçƒ}i-oI¬%T§œ9÷®iœ}´öc)ž´S®Zä߈²­æpÙs?ψJ`6¢E!Š× q¹;ª[ðÇ(Cz£¸Õ¤"qïîüx$Of=QÕ²­.„!ü2j>úíõì(¹\îòÕìH ×ÖéíO/–ù»jqgƒ èÕ§»»-*Ý3"ÇØÇdª¦Ž±©Jý7Ÿxœ‡îa*òñh÷†.Rmß¹ú’w»"¡W11`U·9k˜˜ÃoÒXl¾,zÒ`"Ç×ÉŒäø}fqNÝ; æ•„O³Éa‰\ãZ `öà£Ì+ÐÞA¥JÏ—,ï'‚mÆþQÏ¢&NúÞδ‹b&Iàâ…H›e ˆØ£U{y)ysÙï¼¢ú6—¨{òjó£ÿ"hõk¨1—ÔA¦™‡s1Q;XÈvcÉ‘n}ÐdJãHÅÞ©bî öŒ—†øTï 3šÁýe1DÖÁ£ì*\~ ¿HvTä«g‚««1aÕË”«6aŸEÍú0qn#½²êqR_€ÊyLÖ…ªòác~èšCxÁ!°›¥/½WŽPë¶\²‹h—ñÌî† Ù9Á^¤·†ÄLgfÒÓŇ˜@^\ܯÇûî‹j¾I£Æ×á4}ñz´‹Ó %x§œ¡†ÆÉ£ Y5#ÿ\bë p Zר HKkà´®PZ®=Eã}øˆò hAj›\’¤%íŸ{AfMòåc ÞØBèJ ç,ŒN*Kéâ&tC"©»ëv²…,о9ö':þý,RÝ ‡{ùfºwZñõŠ›—8/^zs0ë_3þßá*!‡©JéìñK-Q@{jöà ¡2±£øÑô Ê¢2cýð¬Æzœt·t BÖÙdRsfÖdnˆ}zHÓ VǤìúf} )FÖ>Òô+Ìíên¦Àt¥5›5>tÙΞNÝÍ4Ôά©J2‘uy–pÅ”nã“ÊØ”ÚNOŸÿ·%"„´ãv„6o³ÝJÀÅÈqobR l¢$É;x`qCÅF+§`˜'p°±¯5¨í‘6•«8›‚…M…5¼P~ydm/ô//¥P®=mÌ”Kü{8/ê¶?ºyF×À”¸¡åPÀPA ‘øh !’™^ÈÚ½¼F"ûóȉ  þ“Õ¢F9bÃѽ&AÖáNøüºÛúQ·ZK×`k¢ÇÒ©·ZFäà”y&Á~¾¼”ámqüÜòåÓÛùi%!JÉÛnxr™©f¹[·Fì+»¾Rç|íÒ~²·BFÈ-L/gwêœ::êˉ9QOjnÄ™ÝcÅ£ÐUÔ™ìoÃæ ™9ïægæb¼ùÛ[‰îÕ~²W¶O¿¶%f62)¤_,ˆ•rW¡Hžß·&”ÝÖòn1¬ÁéÁm‚Q›:gäý³ƒë6Ì­çöc¬Æ”7¨Žù¯/?Ÿ-£=ÄP@Ç^ÜZÖj#oÿ@muñx‘r°ÑUÀû0œâ4(þž‘WÍ •L9-'Wj/vPµT‹ªtÍïÁ¯ª²žbHìs¿n¶Hý-ĕԓ~?¢nl&³%+1ø²Ä?eëgBÏŽwqøp©rx²‡Ÿè2f‰¢ï?.V›"¤Å/MeÎ%“Ó:“ÞP ]"‹ÝA/ÕvŒ„îMEó,bÏ«‚›qW×;¬‘˜Cƒ¡°AÓ=0¡‹ëU£Wz„G]·^w`?nð®U!öjžòÌ6•FÀ…6!Ú>gáç,QHÈ^¶àu¹Þ–uÞÀoîpT1²Òï-ÖÍÿNÍÒEÄP³\1Ù¶ù~I%†˜?Ú|Ãõ»å¢ÿrÂWwz’¯ä=夑ҥO ÖŠ`$v6 ¼o‘Ì£x—ó>)‘—Ž£\Ñ88Àü`ó ½e[Š÷4cºhÖF1? Ò¶FY¯®ï\OMÁ¦x‰ªÓú©P5Bñ¸Lÿ|?sü›+«U=’zì5-Ïšg*ªõH©½C;1rZÖÊ&kçañÅæB¤fèÖOû±·V Œû¥‡YôdÕű€±Ðô‘~aQÄWM G9GrBè |æN›”*ÄÕ”1Jd0ÿ .tWü«¿?^8ªkò|‹`’~Ž˜Íw ˜iÊG,Gб“ëòÙá}Lh[”"Ò›E0ø™•s¿ÏvÖõ*ü»ÍRV¹âŒa, oCiØû~ÜÓkˆÌ»ëü†IÀݤxFÏ;˼m&vYë¾VÖ±v:š%Ø`Ž4{òðËóÛÃú<[NÎGèv¼Å ”^gWô¸U´ÖmÓ)9¸‘«FQ&p_ÔÌ5éQÀK˪Óh¼ó£õ‰ûw4ÚÏê–næGŸ¥,Ï[¶ç9pÙjÂÏ-ýžñO/Ý9ƒ8Ù¯ÏoÔþ)‰iMéåìmV=[æ²Ô[ž ¡5{ÑT cZtE؆òOl£›âaÿ"˜š@¿ìOø¶ú „¾Ðrv™®ñ¾nƒƒ—?Ÿå8Õ±j~õ£qh¯‰ºèü}J‹},ѹíŸå…ê1.;\ÝÅûý4#ï·ù©,Küªü½vGOUnb{G™»[f•§ØŒAGÞ…'k8 ’ò#¤ÃòªÇ{d]°ÁÏ<[N“©Û]Êx”Q$ºÌìüOp xþÙôO¶TôsÎ}ïBWR£»46î÷fÇUeCÊ )PFXkd¸£þõ§/CÌíÿ9¤ºÒ‡L+3T$Q³ñ"Ì~Ä(÷&èÚðÁµ;sv»ù4½lpÊgûu_sĆœ9½š˜°»YÍhŽì$ ç¬ûU«#ÿx™­{" æÓæ''H] 6~ºF¶ÝËþðâÔ|«·±ÆûËì@¹îîª)@æ¡¢«i…°,#I¶…6G¥•‰¦üô ŠÒ"!®Q‚JèÙm-_<Âä0å1É@ñ耴n9Ž<Ýù4—±bÒl iÕæ¿¥KèÌîJ¾pä·‰ÁÈA *¾Qõ2"4³?¼Ú£ ¥dþ&i¥©QSL| v+suã8¾B•á6u·E&¯9®~±4‡n®Ùmõ{ñ‹œ`"Fž’¨ó‘ pÚùÈ3â®´ú!c–]W±+’ \ñö¨4ô`&•ø¼$;]5xiP3y¨Êå&»1ŸåB“h;o™•‘SzKìN"Ю‘¡ÆæƒÐÍ ‚Xx$X!,•¦ €MhÁ`ù/h‹JÝ£‹—×—p9;ÛËΟb¬–šè÷ÕŸ~ý2’­ð&hÀz…Ùœ°äC;w"·vfƒÒ­_¶Ö2{]ÑVâžÞž ‘P_]c߀øü“¸ ¾i—-è•W}@P+ñgºh²·¨•'àre†Âã«cœ‡%0´…©¦í»#hœÂŒ@»N[${Y£dš‡¹Tv¬/FÑ=wÕÆ˜R¸G´Ôi1UðÃ,éìÛnùK HmƒV-¥ x¤ íõšZí"¹QAª‚ç>j×{lp¡Þyˆ°¨UÈ™3)‰–aóéŽñÐG˜E§Qb÷\bÐФR{L¸ã—‚*ǽf6 MFG•œýmá–ªýH[Ó¶™u3 >`‘ÁYwT—›•³Íñˆ«–ý.ÂÑØ‰åK¥3’ €¹JL¸Žoy2jë°í%  ²=kî¶#ÔCÉø²Z^ÿ Ï ¬TÛaèàRˆ”‘¡&óN­¡õëÊTÙÒŽ-ÔÿwÖ°qqWMYÿMùdÀ¢º  Ul¦µk»9ÓpCý]ñû£*6Ç"Äq8 GH›¼a”aú½ú¶Œ>9ý{±Äúì Ž¡|Ž·‰ ´ÌÊ D¹Daw#ð`¯×G3Ñ!%õåkÂóFÖdÓ9ÞÑ‚ÍR~$÷Ù3+Â\ ÌgÁ-ª¨&C>¨ë?«ì¶ ‹LT¡õ*É-@™xbh4[ú· ±¨7Z¯q™wÕ?†a3½t‰Mvɇ©µl º{bx ãøÏ>ãþHoä=ÝÛ#¤‡{4¤.bÁ±h\@…€kß1·ŽN±Þå^Ø´Q ”x‰ôøRð9íÌGÙnÉ‹ƒ³èÊâ5ÓÇÖ³5&€$n9¶«¾qª§®äÎz¯]ÍÏzê{/ èe -¯¯&ÿWP.œL­=ÞY]ÔòtzŠ>ík¶];_Ç‹/Á㺣vIÕE’ÙtÊ}– ïø%­Q—{eýfäÌbœÉP|&³ÅCP¸Ì8.ÜJ]rôòH€´ÊÎÕ S˜àq“]ht=GκÑ´+—†ªI©"w8-¶µ>—77vv‚/\’¶^Ÿªs †hÆ¿ CVX¾ÂcB'ÿ]–˜ëŽù{¯±’«FÍ ¡]Ç5žËÊ:h‘»—q Ò¸µ‹×^~`ºyVY8«fGí U ~F6Y#ó¿7@lB ˜1{ÀêµMÿy!XÚ¶9\˜Oc“Èí¹4íÈ롺ª¥Òá­fcêÓCæ“ËuH›¡'`Á“¯Æ ’èËp§Kcøc<r1ôö‰Åÿ,·Lï±7ìUAñmøŠŽgÏ|ªNÁ^ÝMÃ%õÓñ¡z…äÎöÉ##2½ÕêTq1{Q1u?E„ng›YŸ13º] t¡ËÇáÓ°ª/ œ¯fL†"1ù)Æ6¢ôù¤8Y/Qô\hߤD1 áqì`y»3óíÄ,âyÄNðS$‡X©ql5sº{éU]ßÔ÷{ˆÞne×Ëqëd›AŽYÔ‘ííÓVl‘•$•”Ý”V75T´HW+Ù””–…Äj‡}(ßg‹hó¡¼0ß±À.ÒèÉA11« °K”éìcuÁïºéè:$öeY0ýî?Õbæáõ—6‘YŽyûEÖ¸³d¬?T:¬V3ε⼻®’÷"AÉ]b ª•LP¹=O#èr7uèEÖÂòÙR}hím ôÎO´;äôY‰&Æ^gãƒe£†ï±Y6‘‰ÛÊ=@fSÝS¾· .לïòÀõˆ¶ùB²tŽŠ?*í&ˆˆB5Pˆ9;xFLîÕBBš–Ë C%Hg¼#Ã[´B¬-–™DÉ–\5 EêGb)кñ"˜ðMI–9¦J°Ãò‘ñü÷ÌOéufE“.ÓÔ³"mV4ßRp)¦©pÎM>`eà´µIèÂ%™lÖNî̃AH¾V.Öx°'mÞ1óÅ>„H±šIôK;~|ù¼D¬0}ªäòÖ[Œ±JM}æ§{Qv¹è'å6L‡9êš ?î÷·Ò¸ž@™ZÑdoX¢èÖFÄ»ë=èÂó³bÖáÞ×Ý6\‹æ #шuo¶AÄ抾½m” ó 7–cšvÌUƒî…îÊØÁZò ˜¨r:=POL:?Ó<ªŽþút½ÆpŽ®9aÍ(õKÍV»RŸV[Pfr†;Ê$Œb%”_twâ¬1,¶/DMDB`x÷é|+?™iaA·!zæÊÓ;e…Z7¹¨)àX¼0eÃÂâýùchô7ãŸï=aÎìýw V2P³Yßõñ8‘ ô §jâ­^,Ò+c¤îñ×–ÉÜüËc¸ùŦ¶¬šB©<‡¦ðÔŽ&Ÿ. u¦AGXP‚šæ¬dHà¤3Šx{Rq5ZfâC¤Û!+û€Øk®QöÉ*M± ¸áXgðr5K”0['²^?úâ€æ7Ë}.¼¢>ÜmȨÛ*XÚ³cö©%ߴпJ²gè JfcÚ—Rs_êêOÓ‘ØO mdàê¶°dÙ²³Â“»Aiâ´É ˜)Y|²g>#ií‚äg˜ßÝbiÕò_éèl6Å^¯ ¨võ‘s6À&@ðÓW%æ–6 •[©Ü6Zu§mÐ÷À©Ðk4â´G+õb«`ï0qNx “f__¦¡Õ¶l‘ÛÂéYLùêífc—‡¨"bÜ´U奼 C}˜“®=ˆsPÌŽNœ#Zöï"ß΄Ü}}9E£Ôä™ÕW•zoEsº ¥w­wü'ÎÁPD/X~p¤V„~\HÔßVM}~vnŠb;$–æÙ*jã#ÿb¦%v8r;ng‹L1ØD=‚øûè“îƒHmÖ‘+Áâ‡î`ÛÚqª¤ E¾]=«ôn>qÔ}ç¶nÜ;â`˜Â!|®¬ÐT’p³îãÿwü¾ð†H½&ÁÞÚb\8‘O¡=Ÿå]kLëZj˜J K­Úu É¿f@­ßúä KDåÜt&V$ƒ¤ÖVB÷êÃ4; öÿS±]Í/¬‹Ï'ÜÎ*{BšwppvØ3˜–{eæ$m¬ˆú€«xÏêf`ø|±|Ðè Yì;ðŒLÔw® @Xy¨g¼ú­‘™²PŠÌB;»d§8Ä걺óx´¹KþE¯íˆ¼ñ¸(™þŽSÿ ’TD1\ž zP4$)I;%a¢ÉU€ww¿`×îZÀfWæ#?s¬ŸÙK+D.޲)æÖ­>3rtc¸ÿk’;þ?5—qЙX’ÚÛ0êËAzg1®–Y}>¿þ9ŸxIœ;#â+Jè@nÅ… *Ö(h°çᶘé³6tƒýÉïLP*·vÝbUeÄf·åGÓÓhtÌ¡›î[¬• R“_‡#A¢™ª×~{OoÈVñ¬ ’ µ°RàÚV{ú` F"|³Š×Þ´¨.í°~rLv´N"2õ(X³¯sù"56ðͶq Û]lÅq¯/ ƒ£``‘"Ì<¶UÒ{|¦íÖ*§û 3;Àât(_6¸ˆxšF­è›‡%9ªpëâ`1žw‹l~Ô”r³¹‹Ÿ5‚ñ?¯¢ëþk_Ô)RW<…ÙZîØà¹Ÿ G9 â£p÷ÕhÃ*´;Î)ÑÝ“ð·Ð’I1Ð>d2ÀwàÛD&ÛE6ðÏÕe?+šS$Sà„•$„|¶ º;}Xçb+­¨Ø“~¿öê*Õë„×Xp@ƒ¹C(Åo2 ‹ s]ªW["®X¯,ËaÐxÁã¡“¨ŠÓvè]-ò[LK>À íL®Û¢ü:W˜\ÐþXLlœ`å-pzy"{ G›?Î+áÙ¾¢ÅeG }:ž|¿L"lbØœ×ó„;•žùÈž™°a©›_Õ)Q3ñæû¸ ñjú X¨8N#§zW3Mú] q¤Û“–6Êb'B³~‘q„\®¨I“dàíALqìÏGõ@ÚnàL(K´î¨ØìB zúîìxdf7“ *wgn>ÞfËÛ²~Ë«Úhº¿œ,ô4ž‰HK(kP_ éý¥£oÆ”3]µów=S@‘Vâ:§OOž.VzçæcþeÉî¶k'…耠qÏù:$W¡Ìr@)±¼Þþ«KpŽ)ˆ,"¨Y>­Æ .ô§‰¡³!¸ðúSÓ툉¼Ò–¤£-Oè)µ¸4ï[SzÁ¿œ IõŸF…ø‘ÿO"S>Ù\xr¶\+ÎKMøßt§Ï½ QmÇ®iùÎ1¯KµP+ùÅ{D,[j³a™ Рºõ8ò¤J|¸pıFe£mv²9Ô8Îæ²r'8òÿ;z«Ôã“x¸Èy  ¯±/œM#– D]_5éǺ²øþ¿M²âêE¶ŠJ£+¹Þö¥M|Ü+JÕFæZe4µb1uÑÀ,>Žu$4šûb­6c¿Í1ÊQ·cæ\¡ /®ˆ.?ç=À3$ž¼Œöµ3oÕRêì¸úqÌ?òÁ,xæ~¶W5ô¤@`¸Œ1]'3\H©-Ù » ˆy|JÁ;T•âÙÞûŽÀtÉÄ"Pº €÷+Þh|fÈ×S~<¹6¬*‹XO‡{—@¦NCúr€bAÿ-@ÃsumØó´#õoѴÖUÝ’.‰UÈ)’tFM<†q8iÅT•õòÆ.³Ð”¸ù»ÉdðY5—Á:ÈRt§0Q6_‘ª(t¶}Õ&—Nœ\’ 9{c?»M÷ +2yÚ‚›)0~!4Í_#Ÿ‚I’‚É€|™po3ÂG™E.>iä|~¿F¡w;¢Ê|À1°%XæËûiÊŽ¯ûLëÑWuŒë KŸ ãH—<9޲éoã£ÉÕª™yth:ÆXeYÎ~fýÆ’IW˜ ÆöBn1 ðùd>—;€ÞÝΧ f4—ÕË/žw²z¾`¸dÑ÷Èœ·ÀÕprà¡‚š¨rOcoèŠãô³Xï…ulC§ÍÎE|+™®Ëmð¼Žv0BMy@h@®…e0Mb«©%¬–‚$>ÄÔÊ;*Þ›˜÷Z‹a™òúfbRe°Ìé§©¿$ƒšu)áÈï¯ l49&¥ðz$¿Ü{õŠZ+®ÇÜó©‚÷EåXlïÐ' Ǹz¹À“vׯ™.nràÄ7ÛðÊ‹ðÖ…ßçœU}´÷ùDE´yÃHÉœU•ÌËæfïfþ³-¼Xò¦”M—OÉ0I*µG7ȸÿW ýÜ´¼üÚ·ÝÄ€ç¾ ³C›º³¯ó´,Krcu¢f! `|c4[¢H‚r‚GŽá[]eë÷$çÚ…$¯7}™ùú«ÆÐ*ÏW‚,ØFz„JêϤÞé=`ð%@NUµ(ð\WÅ硟0Ƀ¡¤xŸc_87Ü«¥lÝ/&ʃ4l Æ¿ ÏÒônð¸g¢Ä•UZ:ð%&u‰ã¤ ý=×d³X-žá‹æÿ85~d7}…ÐÚ]¿œžÕSP#Ú²Vû’#›"šçHèDæ|©}NwX«þ8…GyÙ?ð|˜ ¹fæ  ‰SÔ^c¦¦‡ÁÄ7{s+fô„šï^Tö$iw>ò`Ó•|šb<6VètËaVö®ûÎŠŠš½Ù£°…fôÕU•G=6þ=©Ü¿Ì]A±A ÷ûÓ{ÅR-ÊRvAE¹ø^ƒ ^]Ý<¦…"÷gD¦:/ä›Ñ’˜¸¸ Ýš>W²X7È{ñ13<ïõ¨Сó s­5ñt%.^?}ø£…ÕÆ$.cáUÜTÿ)õI-‡ø¹Üi¬Ù€V•JцHY˜ó¸ÍÏYn–9ºèPb¯&ñæÌ '¦íþEÎÒHé}:0ÓIÝ­ëCƒUg#…GÒ"š¬=S8#ÜŒwà >ÌsÎJ-3±F¸Ü¾Ì^öOëvöÎIÒ»ãC— Ï^½xíÑ Ó‘ì2(ð½|”ý1Œ˜(•†YC7RVrËZŽÏÿz‰è¼Y+i³PVXûSVT­vw]CÐ Ûƒõ¡Ú1”JŒÈ„þRô˜=ÉÞžv"¦…^iò1 Âìc€+õ@b˱F#ÞrþÒC‡ß.tïÆ‘-|ìBLT™/X†0L8fL”[.]ãŽÙ¯;TN…î|ä.øqyVÎe")ãR›3A–-ðoÓI©ƒ®JgÕ‚z Î<Ø€Çå…i¿ð'vUëE½wø>·i¬%‚PC1g¾( CÎ*_í=aŠº»ÊT“Ë(ÜmO䋘“Hü!l)tÚŽój7™· ÕedVeÂ2P ÖJ|8ÓÄ©ÞU§_æÃïvÂV*’TɃô† ’å`rÇ  q§žŸ¸`Þ>ÃZo2õeúJTÅ2^ÕÆÛ>_6‚Æ¥a“±<(VT"¦Ùþ7Æ œÏém 8ä“$+È£FAê÷Ïk 3ÖÊl§¹Î;öe1Q`ã÷q'…‚×õÞ“býD0|"ó¹í,ê4°ÝcÉçè¨Ç@ uÞ¸ä“6å(š±æ+Y+íE–ÄY}fÌ–ðSù>'ÆQê¦SM§7='{Ç2Ë܇MªHoòƒ1T†#]5j^-Áe]¸‚ðñÊÌLAQéM·Z#µãÛ‘ã;DüUÝñþ[ð.n­Hô|“¢t~)µq„à©e¨òÀ! ã]šƒÎóæõ¾ z´‰êÇðÙRIÂèË2š#š`S¯ºjvlk`Yòì‡_€ƒ¿Üe£—ʯܱ®[xf°Œi¾)ÀWQ/áù=öúÛ:‰…0³°ÔÛ³+’rÔU6Ãø ŒÍ’Ë!éï ·‹ÿÒ$(šÿé퇞yÏŽÞbqÛ,¬´K¢OãW®Üoëh— b‹ÿ!òäR5:­gûßuò¦‰¶Ûšæ2´´AÀ.+%øsnT4n°óAF8Ûè"áêÆ)áe@WÚŒìàN©áw6±é÷ÒBBœ¨.Ñýd£ÞŒp۫•Uÿu… «¬V›–âõSʉÊuŒ¼kLÎvÇ•¦ö±ÑïB¾ÌÀ•Q ¥“¸O8ÄûŽV綸¥ø[™nНÛ4O/» ˜CÒÉÿàô•IÎhÚÔ˜ì±Êš;ñô Páb ú,Í û,q†ôŸ›æß XÖÉ×7~˜˜uL!ÁºÍo‡+¶ˆÐocX?…šÛÆ.îw­9@mOElΛ¹€áEâÆ¾é®œxØÛ×¶7•7å$°‘×Ãbý,»Ý#öpÛ©†*oç¢ÒIDh±8`è8yF¯íÜpS]oÿÆdI@ÞðÓN˜MVE+Lwú.°‚ `²;D¢Rß±EÈ|Án¡©äkª¼jÂ:§­èš<ƒæË¯4€:•é]î˜]‡…5ÙBÈ_Årpôauy1’¡eãèQAš·‹/ð ì|¦$׉"“ŽISEüäÒ÷<(Ý §!–Úx¹! –Nz¿ÏÑ3qñaà\±Á## Ôý”øLýø’{ö´—g(±CDžïÒü¬ý€Ò%¡4ouàµ;þFËq¡Ï”®s\=WÄ"Ĩ{F àé=ª¹ø-a벪 Ζ¼\¹ì’¶¨-¾×ñ^^>% ’³¸)©ü+­dÿS2ZJvø¥9PéͬrM[0ÆÌGä‚e¡ÊkÇ€´]y-\<&®ô}<®&gØt)d,Å胆S9L¥ C£1½ÃmÎ8kQ+çÏWå¥ÏZE3”!Ã×0ÉâŽØuû–‰jD”"ˆè5ͼ ðL"}áz‘#Û«),#Vpø@ؘÈ i·J€5á깦ˆâï>ÐM[÷ ¤´ÞKæôÃyù :#v:FØCy&¥]çE±4 öÛ¨,‡p„ª¥#<6Ç9µgå±\QñŒ{eƒ:‘­Û"ÿay¨àÌ …Y*zÙ5øï¸èÎë,Sœ1f¨Æ–Ó, o;fýG¾• ïÂd)œ‘­Xš¶±-Î.žŒ¿%éº)sýSU5•F/À'¯L*¯£{=Å û8Á!Éôï«C_ù΂“‡7ú«6=BÌ?áÑÐÈæ ‹ÅKKžcë$ 4ÄíïMAD»÷…˜œAÒŸpçÕÄr‰|h„CÍ…Ç+jq¶‡ •õl’xÚl#à…â‚:Û³a/ã^Ž ¥ôú¾ÌëÖîR†î2¨ïñWƺVòí&|!ŸIñv°É+τ🅣VÈ ²3“ŒßiÒÆœœËÄ;»ér D½ løLð-WIùò2…G9íºÄÝéÒ ùî3×EæÜ^?ž¥;Èÿ‚‹à¹Ã[î7 .lß³'¢÷J£6<ÅžÏa&u9²?ÿL²u›ÚNn--‹A›gyÆ4ë,  K؇”Mr_µ ;âûJÿ]#–àm;uµ7¿´÷ý5_>ÚNus l¼[ÖÓŠÞ›IÕæ0Bsñx@…Õ– &ߥwQô™Ò˜«•á-'Ÿd—åUvKÿ"d%ø\¢”>ë ;uµ7¿´m#|ûi9­Ï* |-š8¦é*“ÿ""2II¨‘tï×j8n*Š‚r`U ü9±¿ ŸLK’kN>!©Ú©lT=UF{ñGŠèsâA>|j3¦ÄëU ˜î‹\/Db³Bø3Á$ƒ à°.íð¯d#ÜÈxþ¤mþ²À ZA©ž)èÞ«ÆÕj;U–“uŒˆð×Å CynãèƒpGØ¥æèP6Yâ »í|šPÅP|× r)±\Si\˜ê y8ÇîCÆ ,¼øHàÛƒ´`ô%þviUÂtb®üú]j¤þj={Ú8Y¸û0œá)› »Æ=Ç:Àåß#Hx‡âÑ ªOõë§‘r+õ·Û·ß×I(£ÖÇ(\u?mk¦Ÿ•ÀéœÞ_øæÄZàþ}L”ÇJÈêb)ùp‹ClÍ$½êŽ#Neç™»CÖâž¿ý½=èŠïÝÃäwxéFh.ߤ ´ñA‘adXsÐn޶ßÝ'x覵»m¯©a~rg ³b¬°ÑZ¬Çµš Å–Ó¥4ͦ¡&gô¼©ª ì¨Fç•s>š,sHfŠPG— ™ó©6oÔ%Ë*âÒѸUR%Ôï§z—ƒe4a‰Ë­´Å¢M9;Œ<Öœ^Ê€@Ýt‘ ºG«Š:¼ÆŸµÙËÖ¥öCSâŠM÷ú֟ǸËûÉÒ˜gŽ¥!Bñ‘M…F]hÔXÌbÄôc;ÁªöNÓ]æØWs_K­T·ÖúÒ$87¸L-‚Lö|¥ÍsX} ±Cû?Â\T„vãNØ¿´ÉNù0z†,„ÒÐ`‚hôE¯kd)«cC'$ÃÃæ~Ø—+N¶Øš(»ê‰IâÙ8¿KÊ4!ê2ÁÁŸˆe¬R ‹¥T¦Y?Z0¼ÃrR ^Šq­®K2ò¡7Di/xv„÷¥Aå›íÅTéra´oÞ+vPG|b1püA×”Šã´Ïöóùi»¾ŠïØéŸrÿVㄪ‘³£GsÂö1˜13H'8lXG†o´·Ñ#‚¿PÎA¡ãß?¸ÿYý<µ‹!¹TídGè¿BÒ[`|ÊÑse9O#ËrDNÓ_£@¤SQ‰-%Ÿ‚/PˆSᆓf®âó D=f4tw"ÔkÅv6êùÔ•8h8>þ”Y~§&Äy–\4üQ˲OyoȇX# »j¬ÀÚìí”xD:‘3ñ3™–pcr—¤õv9 ¸' ‡ìiçAÏ’ >hcúJäfÔ´Òaå“Íc¤÷4vÕ¿#†&7ÆXPDn~ ¥ÿc¥™y9 \ˆ?\B’ëþó½76ó^i*Ë-Žiü`)ö©‰Ó FèåOÞÕp7Ò—¶"VÈ[…Õl”‰ßVHµ2jL­ JU$ê ä‹Ÿå ®È¤ï€"žMNÈxßÂoiTÔ¼ié»ûsPx•t:±—uEÚ×7lV°aïgñ:­¡êzÅóE*uÓQ£.j(ùD÷T•<ÚøG:Õ®ûà'°*z)I›ž}Àë‘îMg³ÚþXæs’U•iˆ²¸š€Úí8”“ù¾¤)ëFyä/{…‘ÄXÊÈã2G°f`ù´YŒ¼ÆŠíý°¬¨€Ë`Kgz~44);jöµÝ”[ó°º|j‡—:9 Ûo‘B-ã²–ìÆ4c‘+%ìܪEåÔl¸ kðD˜wÿ DnÓ3fCUî=ˆôTž;ú4¢<-dV°Ìq4fȱ9"ûõßfwË`µ °L+§¿AÐÞµq0i‚‘¯½J“ãw·’8&µ3Ë  z3Ò £(¬¼ÎšÏ‹å*{s1wØ!æÒ¢•¼—wÇH¬¶*œîrðá-ÐÚ­w\¯î8¡ŸG68ÿó½ÔC'ø«k0]§:ÅÞÈ·U«ÚNnJÌŒìš=“Bzà9Ra „ã+Ä+E8ÛÍXq¯•Ed3YƸn?чçY°µiò²—艵 òÛ ŽªÂ„ßh9E.ú¢º(^.:í„ÿzÿi´šÖMÍ&ïnñÞuDŽ‚CRÁøkz°´ ùÂÆRÏ.¡ÞTÚè#Æ*€ÔóuÝÝõBçoìçîñåfl_ó.TDðü ¹ålBO›LW4œû¤òW6ȯ§Þ›4¦MYø£UééüćÔÐqýyú\ÌÓ†5‹¹:Ôšõ?ƒER½fzˆßÃáÉsŸ¦…ôè½T#".T¾5Qýì“ÎÀp’R£¢"ÜÓ‚È Óµ´ñnò6òšÁÐÚóá¶’ÐKŠiæn£7l‡ñ¶ˆþºþ‚üƒ{(5;XÞ½ü(­ïEiL£XøSнÛUÉ5ÜEÏÕ}õÏûªì­°7Ö—²&#·vþ`àÏŒôåS Á[ ’94$úCEh¡L¯á‰ìéÊÑþç’f§à¤vA{6Œe°á)n"VŽÍ’ŒŽZYzÄ7F×ÄÒ‡ï¥,í7øx%My´^Á†AÆvt±S“ÅFšötýý·n¤ ¢ .ßö‚5R¼Î'IC]ËïŸpоï{¯;/~ÛÊGA<&Í b¨’‹–TPô%’Ÿ[,°Šê= a~‹±6ˆó“ÿK^ZÖòiÒ¥²Á<”,­PaPª)↗}º)ñÙy.IszµV-U( ? §Ù£»òå¬mCrűŸ¾áöÞÚE×üèˆ )(ÝAf•íîàjf¥ 9¼04ÛTž£GŒ5Î2L/5ÒA³£±e“aŠf¡`x™c“oVbûÞcQšLŽ*ÐBˆœ .vÌÜÜD뎘ÑQÄW¹|fz e…v…”3‘Û…ÝøòQ÷N -µ²¢ªÉT1zÄ“·1`Ëõ‰ÃÁS¶U â7œIEw^ÚnÜ÷Ô=4D†)çM©J^ñð=rðg 4‘%éݺE–[ª <è=G„Œiæø;ã…ÝM„qf”…×8ø2ÀÂÆ @¤ø>™ùÓݱÆ&ã¸Y0-±u¬Hû“ž >ß´øN‚WÙ 0šf‰rÞ±Z×Ò#„è“Ô2¨ŸÓ˜HÇå¥1gV‰hB/8woKCCÓ­1Æ ËPõƒ™jËŒ$XJIX¨f&`:†=E¨Ík™áý9?ÚqÈùqNv“ NÔº¼©ù! u¯§{š½g”H6{&ÑÌe{EºEXšM>ÏÑ^ëDh3 /¼ŸÑ ½O_þÞDÙ£‰¸•ÁØ÷Ntó0'YΈy,ªÄK”yŸÜ…Ç ëîasP‰‚»‹w¯DÕ;4NÜñì<¸:¥x Ø“¦ZÎA.Ž-i¾¥Æ|õ) ·ìÖO-Y-DÁ fð¹ –¿±Ïö£¼ˆý ;Jiz[¹H ìIoÑ € Œ=Ø*ˆÖO“º@£[8ý+äæsra"«ÎL/Tëu¦:U$Më;‹0—îžRÊ€˜­Ô¬˜3ö»]O-†™ŸwØw"ìG»8êPv>WÀYcU HjäûfˆÀ]\³½y ñ˜§Â ¼…¾ª-éi‘îŸÕ:î¡’ÏIó¼‹üƒfä÷ia²¯Ò‹ÐRñ=M-É* ¤fd]çOSpÞ0=gêôê^Ù9*e³Ã°Ê2_IãÛβÁêGf~wï©÷¡r@¸•5·B™e²¨§ë8ÄÜñ=ðÂͦˆˆE Åп2…o4õ„ÑíslŒƒÉµ EJ‹v¿U7ð0mK’5då*þ¨Â¶h´l“ÉcµT‚  w¸pÎÄÈþà!Oùîâ9t­§V›æˆÜ*GØc×¥lP§Q…çêA8b“œ9öº®óc™eß•`!yvÇ:뇀+Ï_¹\Cÿ&sÒŽ¸Õ•Yê(–£e™ß8j"õ”-Ÿõ°Q>^æÐø¿ëʺu9¯î¼Ë_,¾*ú·éÐ$¼‘Çhñ‘H°€_¶Ä§ém4•æ)øx‰ÎÝüÚThW5 壘Ž5Ì3ÐHŸÓÔ! ÿ'¸#ý»š¾CØC€‘¯‡fH+[4AÖ:MÔTšDãNè°Í|ð'~p±.+Y´ ‹,}Ý/ÁŒìà_*œE¢$o«ÂøB€Î¾yT?|O­¨$˜sJË Jø©åÆÃÄળÕÇ€‰‚£S.î!6¦.7›ú› ù­Èýòcß¾ùä+òItŸ†÷ˆF"BqWzš-·ÓxËòÁ¤·íneK2Î|"…æ}ÇMr­·ñÒºŒoÿr˜ˆa­§ýgÒŸ¯[ŽOa±•äoNÉV¡Œó¼ÖU^cpÐ}Á‚Ê6«kM¸ \ÔQkþª¸+Çù¿qe+K§MòF½f…a æ™P’Šž‘n/5TtŒ.H‡‡VÞOôw˜æñü5‘«Äè‡ ¿ä@ü(Î@>4"‚Wwãü¸)\Êzty µ5þIh×䉮ÃH ¸1qÚ_sÝŸçbZŽ©\Œ  jC^ó€ä é³Ox6Q_„ƒ\ïVâœT×<•mž‰Ù¬ ?²žjBéh!>Bh v8äòóèLi HónÂ`à@%¦6úŸµše+ðvÀLÅ“{v­[¯¾©²œhïÏú·©ñ¢Bˆþð§6¸>1»‚]„ ƒÕ¹¼=Ä»Òw|@ž£ª·4ŽÅÖ$õŒÀd XÊúëså%ä¸ß•µæ‹ÂãjM©²û!Ý~.8ŸLT"«d-©ùíAùñŽué*ßû?ùÉžfÖÙšÀ“?+÷d½*¢5v)ý˜¯¸g|vHÁbý+–ߤÖÝó[ÂÙoêŸGWƒ]Z+aǩ뎵8¸RdÓÞ—YœÐØ A¤ÊʳÕuÑFú ^Â{:¹ö‹V›ð£ß­X¨*®]:*æ±Ôðô 5këxA5§â{1j¥‚™þËkæur€üæÙ¡Â×d o µ+ŽÙ0Åâf‹è‘¿fu®ÆU­K[—ê™Ô›ô2j#ÜðÑðme’öKz§¢º¯Ð5å@€ "Д»°A@¡Çž‚Y'‚%ãt 'éös…“*]f'^``Ÿ%±ª[!ʤêë¡þ{y˜+¸R^\Û ªæ|éÖ‡S¸„¢³ŠÆšT¾cÄ̪0п¬1Ñ9ÕTñ_hh5\@ýï¦_óÞZû-È{ìÄ«‘gkä™PñÖφÐJZÜTÀlÔièzöuª&!kOzêm0’Æì/»nªÅõƒ*×]íÆc•-ä]‡tw{”áùdYrå8£`¥eî^ î»E…½%:FK¿æï‹dáÍ)MÇßžoÿM"^.‰Â ü2˜Îo‘«1©v+_¬-[á¢WD‘m ̧z×Ü g“„|!ïü3_ä$øÙ3Ën”ƒÔ˜-CÄŽþEÏ·*€Cœ°ÉÇQõ¸Ù™Æ…P66¿H6ÇÌl’kkiü¶ÕVšW\¿Ã`þÆSsÞ¸Ö@ÿ<†7”t^ÜèöMhêL§Ér 9žÑ\p7VF¢1ÓR¯Õ¤u:B¦›×QŽþ1Û½z ]Bj/A†3ç¯gm –Ä F4A¯ÉTâˆ×€BÜ䦘Á+@(ž»Ò¢ MÌpÝfb¸J€‹Í½"¯i-¸9áns› ,PTÿc<'QéUò†^_P€þCšR“_ªè~‡Ìsü ŒÅ].ýQ¨Žå0ÔïV9Ê£ïiJ[íóçHÎõ4ÍÃe%|Ñ4ÓЙ­‚ EqEgµ-~eƒÔ+=–þ’<ºŠÏ›sû…Iψ86¤Èh† ßg¢|HåxÜ Xj€y•ÁÃOÛ ½-~—ˆ÷òïèN”åýù|àè ¹ý"»Y<ýC¾òši¨•éSk’ƒÿuŠŽÑdLé¾0RùhÙLëÜM÷— ­1Q?î"Õ°¼ò±"øúÃþÌ|l_vsž"b8"U¨ô”ù¯Jžz^ŠóY}® ÖU"@pçþj´yŠÞ„ê¨fˆµÒò®/RO¾fÔ©¶µg:V p¾¬ó“å;oµ_BÉ—wÔ_,±».vÎû9åNÅܼ"b‘!L“¤¯N×Ãç‹Ñj|gâõý³ €ª¶è³Ÿb󙇊B»Rˆî…?)˜,µ>âY“zë©mý…Mù˜Â~§˜ ç¢{ÃV“úêè¿ÔÐsƒ[6èkµ6þÛ^úz¢0Àé¼ßÖù'±Mí˜g›ª¤6b×]ñÞ«L—•¢} N¦ì¿œ]ª%^ fŒB))ÔƒÔµ‹°õMæ÷e\¤á*J"ß5Õ«&„>êfש»Tá:-º!G3° ÛÒÿ+8µ&cø+—3,Ÿ^3Ó’Ôº«âù±öÏ(Ü¿b哊†Œ\,¹_bÀ({ÿ/Tz3ËŠ–™åÅp‘¼§+£_w Λ“*e¹ðS¥¢•鵑gAcÖƒÝèhú¯ÓXÃÿx;µYĽUW’à€U/–x€¦µRš„ Imk‡Á9 ˜Þ fœ"Þ^¸µ¹UL÷2×+!â fXONK"Ž´îç€dóIhI[2¾[ƒÈÑC_9Ç¥Ã:µr6t)Ø y7…œbaói')Ø$é²q‚ð;jî;rx~~-üéäy¸á•áx8ÑÒ²ÿ@Ÿ=þôÏ¿zŸáÄ8¸M²‹Ê'0ÈuSg´gŠ ŠØtÊÛê1ìÖeÌç$¢em\süä±~?ìÉ»â¹ô6ŒŠ¥ˆåÌÆ L¢ì8"]ÇäŽíL<%¤ëUüyªÕ_á…áû°Poû¦øtÏòjÛ°>§Ö3(¦\±'UìÑsë?ÿ X{( šÀ¼ŒÉ½)6ØÌ ï É®táû[ë „í,\qç½Ä#.ÚCÜHæ\ʹJê~ –Ó/°±y&rí~®+ìØ»º8m§3”# mH¼ ßÞ]"ì¥Èö5m7µEðy0V9}O­ {šNu„†Z]ÅYV¯jŠíÓäý´èÉCÕ¡¤Î¡€ÑsC³Ô&ªBþ“ÇÕ_LvX3Úo¦Šgw°'rÝ/…WCƒÓëÿ}Ðê²çeí¹MRÂf’Á‹VjŠÇìÜ? UÜfNE|Q±:½ i­b}<þZȨ̂OÞÐláŒÁ°ã{¼#nØ€9e•hµ±â!ËLÛ:MiãíF€(¶åªž9Á$ôÌ{;U˜_`·èõþPÁÈt6ôÁåc{ Ÿ´ÊÙâÞ˜©x í×YiR,)¨ÝwkÓ¦ +Ieµ4áœÝ—8v½l(g¢Eà·ù=Öò߀¢³]àWläÝ7³¡;rDQ=-ì îç"ëì1xnÏm,˜îFóMWsŽMŸÑ…}€5Û¨L…<Í¢Üá&~»nô2¶cöz|6³›7Ú])7—5l* ~$m©ü2­tYhà{ɉAJ¨V€lôÏßr‡yÙ~Äfí¶D2CÙä+×ø…Œ|´¤7³ÇÔ7ÆLùra ¬ŒÇ“xsÕNfíîòîdgÁ$+¦ù¬Ä{$dK×@h8´íúMÕõðä,àg≾M‹š>3Ô1Ãç qt˜j¦Úq–-Ñk@ekÚØ4eÀˆÕ¨#{4»÷Ü˼"?­Þ§ ¯Æ9Dtã`4™ò@8PæabªscD—Q溨¿MãÀ¨A ñýÊ^‹Kó8µÅ«ßa¿Öš#ì6=}–¢ëb³mwFЪâluà |ªKµñ %Da¾šÉ̸ž*’Y}gÿƒ¬½4”¦!€Úx.N-:Þ};ÍT“$š,´Cq§s·,ÐÑžÊåðÖ®øÇÛÁv›¯]†ö»åýŠü\Çdâñ¯O—³‘úB2 ,ð”B¾É<ÍØkiS¹ÉjÉ2©p^r£0ƒÊ€Hf«GV›BEc)x4² Pc[Pdzxá:éÀû1&€Ë²Ö.*©ÚÞwԳБ•µçHßo"}âèbq×ÛycŽ—¢f,(x?,Sd_ µêý³›g”ÿPwZx(3‡[yï¡Ìyõ³öÓϦXÇá*ž)ÂÉéj6û¯ï¯iÔA—wëH¾=­å8åãXgÇö,Н ›5uÁ;Ï/ÅgèBè…‰¯œv$–/YÁ¤¤g´9Ôܱ—4ÑNTŠo9(ô8Ûm}åö^°ÒéãñDG¥w]1šl(žv_1åòÿ"/LL÷ܵÝͰ ð 4înØw+m¤< Š÷%ï´myýâE­ !’î8ñ[ñ{óöBj•?évï4®~Ü „5=±ÂõR{@á é_•Ãsj>ý‰äçÍÈä¥ekÜÊQG 9^ ?*‰¯úXwzÙÊ · Èý(Ñ0@àWù^÷Õ‘ÅñO(ˆÏ^È]åGÿ}²î®Ù¡¤Ë9ü68G¤¯(ÃCæq¨‡©û¯•°ð µ[Ïí:òrcÝÉøÅ3s±ÅÍž…‘.÷AŸõõ·Ç~uºqõAjÌ0»ÿ=–’¾œZ»dXž%[DmÞåÀ’É"N3ñÔsÆP#´{{*@vø®éhÏ?E‡6úXÏæ‡n‘}&ïžËßU‚Ï~¶o­Ÿ,7¼€‹Qã ChYp"`é:x'µt™ˆ¾Šor9ÀîÙ# øêBû-ÒUð,ïäæwèEB¥¯Y'» ÿ\ÝÁµÁ·×9ìNϾn n}A©16úO ¼gØÂ.Zø'îÛ(@¶Lñ¬PˆŠ÷æÝ T4Æë¨0c…› €å8¸±˜ÆqÎ]¡9sÔôÐh)ˆÎ0êš$h‘oºÏXÐPß[áÞÙ+É®]üNx¾UF÷7*%ªãÎg^šêìˆÃ¢¶ŒÛüê$}x2[”½-n&p“£i¤Åq£ õ{Át Öˆ§ÏÌ´ôÌÚ÷x@ÐÖâ `lÂÚ¼Œ3•-aÂÄÇ'Ç$º~™BÕwe‘ç "É„ÑxK+a'¿Ì™&á b>!šðŠ<Žˆ>ÔÐW—Ý}~ð¼½HŒ ò¾ïMïÔ2èBʪõjøCìËFôù*e\yPÜÑËÕü$€ñ³æ" Ú™¿.ÏŠøgv’qÕðÆ ¥ ûíIk—¡8d£ªü’îhå ÁE”¤‚}yÚ§‰ïÅ…³BoÍYœü[Î~´L'Äž‚}>óü¨šÞ'a‰Ž“ÒPÓpÉÂw Îè6*'‚?w¹I+hΆ¿6>f~AJ åIâx¶h8³é­°þõ÷*PnP§{RØQç©®ž,3E¨Ñ“«§{XŽø-Qtª‚ m”Ú´$Yç4Ï—½•ƒÜÌ"¡5Én㬔X%☥ˆÌ# ?Æ«šrQ%=hrC~µ½@Q8ÊÇ£ÃX•ÿ0j@Ï$õr«À2ÒÔvF>'ÃNþŽ{éÕw`¹‚ih0®2`”¯ˆT‚ßðàjÐ"ò*¤¿oTGÊ›Ç Éx“êÏ™è^ÖûìÿÖð-joÔÉ)ÝðjŽ¿ÆŸ†•´‘Y-“iCž@¶Å\åÝ–nÒ%ó[ ‰oÙ¹–ÖîC§u5JžhìÎ÷±ÿ}ìÓaôñÛñIÈ¢½ø2¼wq[8º©!®»Ñöà`Ô¬S´Œ;_ÍGQV@òÃ4C—|¶r8P2WaêŒT°½3Q^ÄZüwzP}ªY~ް®ÜN²¿@!å/mI´(O°ý:î—ÃöN_ñ¬¡ËSÇ·óUÀ\ň |&»ô“ï!!ëä£NˆåÕrºtU?…1ü½™¾l}ý:äîáÐý0&‚f-郎ô¹!öàí9סÞÿR…æS]j6÷„½‹/^ªZù`»É-¾Ö1ÓÁÓÁ.Ó8VuiL»Ýòom%ȬÏÏÒˆU1æ˜zÅ·~¿[ªò 3 …æ“- †V*R¡uLåq‘Â4·9«– .3b©›ÕFøî@²!òéŸhNô`q‚°¿¼žÉÄ|œxÖš»TëUW¿eÛ>·G¯¢A†¡a8¸5»*$ªÕí cE=i¥|˜J3"÷*¨³c&ñkÄsZÓÉPƉê^6Oö‘?Nݼ½Qº³ÉY5Ù‡‰_ô¹kΜ½ï…´>lÎjø2­´dáå"`ÇÑË*Kô² mðb𮀱‡¹0MµOÎhÚ¢J––¢Y]³ÿnÌ(ÍeDî/)ž˜¹Ï8fÈí*²¾¸@ŽRÏ ˆm´”ì×Ñ!‘éšÕrO(»=d©ŸNו=²èê´±³ü×c狇 ‚Œf2ùaq¾ŒLöÜ£²ìv­’á c®m¢`úfg“åŽb-ûÈèãõª!ß2ä+Ì=M *Á÷6qÈÒ<çC“iæ÷ üMÒ˜¼erøÎaØÒÉeH!:Íö¶†°ÄyÒ乞°_J°ÐQi˜ ²mÊØˆÇ+šÜU“p[>ÝÖxçK«B“{ùKZÓß8â?»§å©ÄLÖóÔ¹ªöâ|€E4àõ!ãª~OÇwÂzLknÕXæg†—bpï0PÚò*.x>êê³ÓAdÝHšl×øxf†%¼…¬ÔÀŽçñ2g¨¦b¯6éƒç÷ò0uúó´Œî›ñq¾â§û0-Et$}£Ä¨ÂÉÌ`´Íñ‘—³I({ãIº êô¶°l"»L/ŒxÃB“»õ…øÝ~¹WDõW…ú8}“ïe»²¼Ð²~g5ðúúÄ/ðÌðÐWAÙ™²m*< g3)ë»óŠr÷ÅTo»êfÆdé5¿´0 3M ¬Î~Š<0QèNÕ>%ž^ªW'ña:aƒß ò+eËy¸å¹¼Ç…’?Öø(q#ØÔ¦x›]"×½6¶p{ùuî´Â'T«ì…Áóù¥N¼–þjðý,Ïl§2ig¿ÒBÉ\PÃÇØr²&·[õÌÇ0o3œó$Ü ƒ&´ß£öG2V}« Lg•?Rl3éÜÛ«æÚ:? Ä\ÔÂ/ßíø:½Àhg…;pc‹_P•Ž8i´òBößÌ{n–îYÐób›è–Òp*c]/ZOÉÄÑ«D“1˜6Í(_ܵéâþÖìÜB형é6ûÃ$jw|ƒØêÒÐmÜÇ×FbS©‰W!½P¶s4¿)Ý×´Læù““­`3dV¶Ú §¿-’ÙÎ{â ^ÂU˜Š—6ù¡Jò‘ì(Yqä—†ý²¢\Y’›S˜§evºHHljÔÍÙlÃC9Mâb]´7hYºi‚¿ü"òʯÜFÍ…‰&q—(ž×õâhÕKX?Þk=¹ruÓïk ÈSðñ‰ 2bf’¿t›c”#ÇH1óµÄƒŠ/wWŠ©€Ä‰˜"Œ…!ИKà>5OÌ=í­CzóÍQw_ÐiÕºßg'ŠÎæ6}cxÒOhΈ„0-r›„%´oÅ¡øY[«®$7Ñ‹%+úòCçŒnn›£m$Ks»?ì•ÈÌ[útG3Fº¼UÓ4¿ñ¼¾SÞíë#:Š¿G@¸vRê=.&×âf:°˜˜¡m:th s¸¸ðˆÏÆ(ˆÕŒ®Àç5’<‰“R2]õ*û9àä8Ê)C‚´#A˜O¬7¾äâÆ;TA­ÕÖæ˜…†fÂá&#ßÞ¥¾´›\)—EzF•M]‰Uýäj”=¿ŸÙ<Ý‚_dÏŽ»{`Wj•šÜ„-V‰†`jÓÜÞ'.±ÃÑ‹¨R†úš‚f„P¬$±À^Ô‘ÙXH:+2zùËŒ‰‰>74<2ðVƒ<ôÜ}¿:2À¶Êt?ÏÝÄgîMöHŽôÍÄdùPsK>ø®¯ Op \ALH€m$地<Ʋ/B­&øÕsQ¥Ò¾\,4Q ¯MŽ™¨œ…˜È^:÷¨iÝ#-{¹–íqïàú:€Ñ,  w¶DY¬ò G²?>,^(]ñoÃA.ìlëîx“Xci\I öµÔf4±2¢’5R£³ü-8ïn~)Áy;.ëGv7'R´@’¤Îì¢*ã<Ä8Ø`Ê´!¨½õžÑ=åº`r‚ê`ðõçúíIÄÿ{nÂpäOš'&Bd‰4@™sî ûêÁqÁ_e•ü¶Oן§p4}ø—ÊL  ~‘Í¿™ÀÿÿytQ£xT `¡k³éëí¯nJqBéÔzZfT{ÐéÞ¾ö¹œŽ4 é9bóAûó&pPÁ8çŽ)1¥f¯zðøId…ቧwTG£L`2z5&üYÀ´²7¦"KÓðH!«§¶,ð˜$Nвá‹Ú lÀc˜}D¢¥kxmš 0Ì-È•„Õ£L‚PL¢"_Bìx°Åþ6™£³TÀ£Ž.›³Ef€‚,|1Ûy7FsC-¥éq†ÍŒ‘võ¢‚ž*ebµ\a'”*%! ûJcŠ^2³æbÞÏÚ‡n$xF÷§(¦^®íØóa9‡q#SA Ü,Ó¼ Ô±„ƒ @ Í8ƒ¯Ó$qEø¿|NnvHÂìŸoûësZ€á?-ŠëL a@À–¶ÚÔ†ÀÝàÒÄÍί JÃ6ÿ.Ayg’+kë"×;eÃù–gµŽÂ ¯¢3ìÿnåüÒq .銅¹C_ÆíæÎ{ ED&ðÊÄ< ÔQ %©Z&§€^â~Ãë-Bb~Q¶·ýÆôÚÛò§ÿgм“ ÏgLú±üƒ—Ý0oú¦™6ÿë^_ƒÓŒÀÙòºJ%›­v,ipb~‹†%a3 F5ÙIA|èJQÉ€hÞ}T‹™ ¸»¾1IY¾)éÜ$©:Þ˜µ¡aJ$ƇÔ ÿ0÷0·A-ÆOï¦ÿG݇á[’›û^춃µÂ±îÓCÿä[/Û[뢡!lEh ä§d¨S+_ìâë*B%‚ª¤pÛIL|y®"k×+#,ÿ/6PÉ­öÖ¹ – «ä‡jŒ{}ñ®A·F4ÔÒY4£™Ví‰Dëîøûÿ@¸Û0yôÐ!¯7Ja³4nϺVñPüäÂ^½\ÝŸÉg•°^jd}X»j±IÜžàÕÝ›…=¸ÀÆNsŸoª]ò5F8{dÅž ÄÌGù)ÙéJ?²5ágµ¶JŽô Ö"U‰ ^L󡜕KÚpŒ*`§AÀø8×j5°/ò}äXú¿xxÊóõcj”By`w±±b°ñ›è(Â{ïÆ@Ör¾S܇,³u¯SÙy*¾ìÑä™°ÅFÝÅrd"☕¯ÆÃªÖjù+DHGÌ}*†B]F/KäUÅ<¾lólvE¥»"Um ø¼ð+·Ù—rj~çÍ¢ 3™áçó>–0Xª3Ò²¢M«,`mo½ÝÔÞŠ!Ì|Ëg°|Æ$#Z¦ú­ªRvXB9Wò9ÂÐÁY[TÐ!-Çu3!{âãvØ„—•ùcö9‰1±J Þä\#6f®©%:…¬˜ÓË~TéÙÒç¼uEÓùG¡r’Ãd{lKi­ÐÀJ`ç wz%Œi{x¬˜˜g~þ`tKqîhj(z‚°¡6ý!'|™£[ÿ¹+3§e‡ý^‚ô#ÀW&GÈèKjn€7Cz†<]!DÙ¢à‹¸Dµ­âsÈ¢çi’ ñak¡˜_8݅؉˜‘Æ'“g`|c>×e* W0&¨¶4¼U‡O±“+fùÏÓ+QýÀ6è|ÁªªóÖe¯æ4¶öSо›uh’_ÖÁ9„Ø¤Ûæ¥üAGiVOW«ògõV E»f9“¶d]±ˆ³4γ•»‚k{30J¿F&Ãäj÷íL4ÄÊ–øÖO!4ByFa‰“±ÿy+»gA¸ï y½»½,î0b–i žš{ #c¨?§2Í2"Ÿ/gª*v°õS˜j¦© ªg¸÷¯\ü5ŸÍÓêeZ€gm°—fWnœ_F@Ò5,Üç@C°¹”(‚\AB¡a3Êœðó{¯óU”L=Qÿxï@íA­qþ„F+”;ôC <£è€º£þÉ%€e 6UW)Ùìd” ÕŸ™U}¶»ütn·^k·ýJˆUyéæ2¬¼æ~_jEU-§Ù=ªe¡ȵ$‰oIºÝ~å‹ÇåÓ˜6l¼ÎÁíE_¦_r×Îô ‘ÏØäÚ¬’—ÔWºüú@N ½†Ç+z--¶6êí[… Ü?F[…Ñ0ÚÅé£wÕ-Ed¬Þñ´u8AhÓV¬ü“JÐ*Ô0)ßÿoU>=±D¡–y¥@¸Ÿ õÐ~fY½u•¯ k§Âœ»ã0éÉ‹9RsAq_…±dyšvU’Žõž3€<½œ›­µoË;«6u—ùÍ9  ‹ûp!#$èg%*~3º®”ó˜)‚€Üá‘ ©o@àhÛiRª„p–-`×òI& îÒ«9 ‰V¤–LçÛuÑ%¶ÄÚàµ=Úÿ²FÑgJú¦öÄ©ys9^²ûø<ŒoCÏ §¢xæÀ¢ž3ûOséçûÂcd*§ÚŒ—¸%Ê;ªõ¬…œPG™PV¨Ì#Ý€@»G9¾ L- ­ûUÈ߈¿C1X'œFöúFΊE \MòÕÙ–p†÷æ3eep»™¾y5Þ'«E0ê·ÚEy­j_9׈‘ùq­ßÒ— VðR‚鄊à2È¡T-áìWNšš®@:e›y„GûŽGúµJX¼Ø¾ ý—¥üâ±bwÅ7sR t Ÿö§„Ð@¼¨b»ˆsèþ›lù$3äkßñºŽbz~VGùA{öèz2Ö]Ò8š”¾Dº­Gûчº©ê¸Ò~ë–aJ/ãÅ›AnÍ?”U—Á‰¯†¸éÎÖý‚ûOÂ¥¼]|¶nâ(¨ŸÝpöÍœ¬Î-"^i.)ð˜Šu_K ÆPà±Ó@,aÅY«•év»‹*g_NCð¾tq ü±åÔÂÎMê°PiÿÑ2aöøu nÍ4˜¿æjœ¬ƒê4fz)$h¹;[øR2¿rÝT3&xÀen=ZñkEf¦ù p¸i@‘»­ ¤¾}ÔÀ®e>ðÎA¹vÎŽWª@—+S-‚õÞ Eýò–š×Ø–xY çjÚ"¯cÃ*”ÅIÌ·¬ú4h2h—)'Þ×=ý#vð˜éd÷»¥«@/Å“ ÜãÁaEnz¢¥5ù¯Ꭰ ÑF»„Á…”VÍj+±ï¸ƒÆûSh“\ÜZ_Ó£Þa‘¿Mo²7µxKß´ Á@®__œõ6ï;[µž øö.¶UbèÀ=ŸÓµÞž®ÜCЉì–yi·Y]Ã-šº˜y£Ð¡®»DóÉ<8åí¿LXBJ`âuCìF€²8kw°ÇéÇɳ±"]ÆâŽ”_ð ³ÁÁj¾ t71CRˆQ…ÚÅÕ$Ͱ¶au¶RïÆí//ï$2“÷¬Ýò¡t¶´ŒMÌ'ÞÓöºÍ(Å·üÖI­zÿ>}ygØŽ·©âÂ]:nô`w#SN¿³b´Í\üÃÜølTD“Õ#íŽ3ù*Y%òr»~”· ~>f\R"{­-jV63ï àÒg ¥ÌPè§UžÂÈ‹C€!;?Ò³HgidÔý©ÀÚ,ÈâeaFÜ7P¿Lydq9%eÛúÁ® `†È%, wVZFì„`§®AÇ &]Skçó/nÅ¢OeÖ(Wjœå@•ÄR*²D ƒÚëÇé…ß]Ø ßÆa¯E¸Z;¢½€å§îeþ‚.j|T—v@h‹¼¹àð‚m¥× 7÷C©âáÀ²ì(Ú)fä“ÉY€hR<ÝEî¢k žÐÂÊ·ºBŸ14³{_ú÷"_ØGMY6\†bØÔõþãiôúÑÊœ(bo±7¿J¶@j|‘.¨ÒšU?1ré\$ûí¹(§à²b §j ÑÌð‘!q1‚Å4M2†›Åçñ­•š-¡œXl,mÒ'­q¹˜dó—-Ò:@N…%p¼lQ\ZˆÔ"ßvãZ7\ê( û ¦SyÐãp†?Hµpí%7–dÚpmŸx³¸ t³Á[œ9¥£Wp MJˆ†â?‚ Óû&+:úeõ§Í®a"N…\¸J"*$)EŒ+O"ÖÙ—ƒ¡=ë'´}´„ò{ÈŽZœ@¡ÜŸ§¬…ll²‡PŒ*º·AQÅ=ÀàðŽ½–òÿ^2Z“µ;Ø5aÉ$ÝÔô YhbÀiï†vá ‹¥:øVò\A²ÕÞŒl¸~Ù J¨H‰Yÿ^$Ó¬÷mTu Åò“㘿ïrSGoà¢Èn“ÄC¦ÇZçJU>’b²ŽêýÔs+Ya’l…¨&èX–ñ„|TUÜðƒ;Úi€ÝÃl_—ë ÒO@åÞ~TŽómj ±»“/™/6Eÿ 8j}´„Ħm-­^™=ÞŽEÀ6Wç•_ÞI…pr¦(Åf H}>c=*ìÂ*–Ý ðsH‘ÒÍ#²; `O}mˆøƒ©z²Ii­Y –ÜWÝ^ù}Bfï¨,HĬ«Ã_Â-ÞÝæýdóì®fÁ ¥ïô'¬Pùøˆ‡mý˜pö#ü æŠTÀö×GÀÇ­9jÄUóp‰ˆ¤e',ó)™ú Èì1=õ¶#àN þ¥ë É$!¦µd*[q_u{åõ ˜ §ÎmQÇúKþTzêÖ—P*“cϤAè:O~ *`øq r-¸$=ë/ ×ÖjlaWýÊîýÐÚ;¿ìªÆ™Hxv åUÂG¯9 Ôtæáo¾`æë€‰È¢b;èÏsÁ{Ç&ÒA=êNÕJÇ#­8ìHJ£ðÈ’„ÝÅ,+J‘kG¶EIýÒ¾µ¿?ãx‹)P?Öm¬¬m>˜#‹¸>Ú²y/f†$ï–r4U.7ú—¬+$†šÕ©mÅ}Õï—Ô&`.†á³Ù^Áꪯ„Bé¶i Z,×l®žd+ª­X~…9}âðîÜ ªi¦þsÒ£Jý`PáìX¼p˜×‚0óܾ?½¿+<ÌÏÊï~¾)±{ö†Et”(QBÎþš‡"à–lê}h·çÛwCW@†÷üÝwÄô¡´‰ÍÚZ£í zÜòz“üO\ÃÌ÷Ó ×:Ðb«¬£äs7(â²äöZà§óÞ¸ÿ;HNÙ^`NSÍÎÓêƒ*2ÄÌ/ǯ@14„7–ŽêìP¿ÖF´‰˜ §cÎú$Û5áî#Öiÿ9*ðíáêÍâšÉó¿JúŒ L/¯1ðƒ#âì·5‹ð4>­DÛ$DÞi™‡yqWgBjp½dúµ}ZÒ ¼ÈõÎyÃ>¹A Â²þ8!×ÅÁXÄu c°+zâDআÃþapV•Ÿº†œÖrz  Xs )Ÿ{ªÓŒÖ‚‚­Ì¹ÆŽ p  >¾¿1㜵FfÖPð»ïb –ºHÙ? ?o?tDcÙ£½}r “ÒaZL’^ØpL¡ËìUOñ¹ŸËu…Œ(Õ hkñÊUÑɃÂÏu³2DK”RŽD5]Ó¬19:Ë$N˜ë÷I'¿¼T–c4â-ôäëU“*œ¯‡áWÒñϰŒY×—_ZY#̲ÒZˆÑoHZzռЬeXΊ 캆þJÃGLêc¿þüðÐ)@@7 ‡-Ø%íL ̇IødçlÇ ;9 ÙSþÔ‡>_XPôy¬Vàω@^#£¨X~6¡Ü—ûÿC|à6¥¥6#rŽŒ€óßÚ“ÖØ:$Wá»Õüd!‹‰Ö0©Ä™J¨÷ ¯Áœ­áâ©ÍwGŽÏêÔÍ­ 3ÝŠžŽ¨‰â vbYl«£;¹—k;è 5~B§^øÔHaœ)%S—J4‡q2ÊyÙ$¤÷¯Žèa¬–ppž•ÛÓ ]p²°\9Õû”ݸ;ã¿¶ZCâÕ¹C²†@ÃåÛ¸KµCÖx‚¦¡˜²¹KŽ5‡†±*=ߘ.!ߨZlú°Ïu.^ ª­ûaþ*É&¦j†ÜÉ9àù'×{…Š–»WÌ©œ²T^}“’Ü÷ Ž`µ³™  × äGw•È}Üø'ܱæµr0!!kAC‘ïÏŽ7wÀœÌ—‹aG…òhÛ©·I{ž—ÂÊ«œ-(N‰†Æ‡Σ܇Mn&¦Ä}èCñà°ÙHl¢\íPRõkÓ²½1ÁR°‹«GbzÇ졤z Aáë‡Á~úî^‘­Ų€YfßÊT=þVþÎ(£~)K½׎‡ wp‰Äo×Pj08dÊùñÉJBj…'¼”’Ÿ9Õc6ÿ5šcõ<Áߤ—ºz9NUíDÁVчVÒÕSPåÐ¥€Ä._׃ö¼à”ß'…BŽ7çTD¬£±×<¤qûNs%9=<ÿd¢ÙMÞØÓäTÃãI›aÍ]ÐúÔ€ûk’l2ÓXÌèÌœ·s¸ œÂ[zÇêv'2€Vý§}ð]–b»%!-¨4±¼ƒwAÝ“ÙRÖÃVo~НÇYšwùãÈÕèÓÜÔmê1fGs…Ëj>Il½${_6w'&À¥z%™Ù£ d™ÅËÂÄãþ(.ð1qü®†‰Ù«EÞ™dÔº¶Æ¼kkýLz0ýïfƒV‘Cš›‘FÒTïÚÏÍ*Ve:á?åZzô~ò ~õŒ7~±Y7÷å.g*<„œX•3]'élô¶ôM9Ã\t‡×ÃY‹2‚'úd®ü¥MpgûLçKÖJ|ùX߇6[¦gé—ð_Š?®~A8ù§…o ×–¥¬i×RÐç“{º!ÕÒÄNb¬="ã¹ÆÇ&-sª–dç†ÎØ5wAÆ4ÁÊ ë²–%) ßTªát_háÆF¹üŸ–ÕCÎß)uõâW;ëÛÑɪ¤ÛÔĘE€ÐÔÉ$M1ò„½süµ‰[SF¿wú¤VZ]ñcóIñH‡M÷:î©nU @'Fì1Ž,[Xõû»„u3ôê¨tO×ÎËõ|Ä”–^´ÿCW9.2¡Ã(õ¤š©Èâ» +=ÜÒÀ¾$f€YãôÃ5Í8Nµ;é}\ï›þgd&ã „¢žò :„ȳöõµÓ ²•ÔSí–j&Å·å}tíû;es׿mm¾á«Ç„0+Ê ¾¸€ ÜG[„%0•öúW>c3é3®…@ƒDOg¼vÖnʇY ¶“x· nçÌɉö”ÎDÆqײ`Zä7lš«•d|ó’ßrú‹ÝU0OQ=¸¨d›ÕI#Hõʈž¥ŸB,ެó»2ªØî—R¹×¼Œ¨ð1ñÔUœÀû=˜è ¦<Ã]Q¬Ðµ†ö¨2QKK‰±†¥8Q$‹â6MÆ|º—|6É|$“€ºÒZS»AXh_ÏME]âlñ¨âaµåW¯dEvÌäÐúé<,-i…zE€1‡Ÿä”B týÿ_ISb7£¤ÍM:–„cåÑŒø¹ƒlDmé‘LB1¯4iÎɃÿÁH.%Ö|¾(Œúãø@ÃwY&Éž­9‰$œÖ’ÒÚ ÀÃCgWm»s~|wá0Úò«×²"»frh}tž4½"ÀÇOÆXÂêu§JØ Ñ_Ÿb\¢ÖáPHÂ|HàtBɶÒ$²²z_ÊyÉu:ô5+û0 >ÆFSмêcå:µÖªäPµ–ǰWŽn“UX4.smEN *æ°mXTT¹­µÜ+Y —H¼…@ «i6È•¬tOŠ1ìÀ`Oèz,|4ý{mháPE>_ß¡§¯(7“Ç–/¨´6Š- ÔàðÉT¯g#0ymÎLÕÓ•“—Å@ŠíŒ—~ ¬ñ:àÞ‰R Ê ÔÓK±fœ§y<„´‡.Õ¯ñÁøõ*Î%!¨šöÕü7{ñ Òjü¯» ) ¶ÞQÌ'á®ëZÅè߇n¼<ßñ`¼QUZF¯»%‡1}’¿dI_£«°'7ñÅž«¡6Í÷\[ ´ar@T©eÉ éîDèXãlkÏú®¨§[ÿjkñBOó$›[Ò^ëâo…j =m7þ¦z©mß`žõ©d®ÿ@«ÂäÏ)Ý»P4ÉC[œN²~%xÎØO-¹ìÚÄçþ†×•a7TŒt¡ÇùsNk¸‘ò”™†)A¤Mn"¶‘¿½¨wמü‘==j¿˜™|ÿ`ó5‚AŠdšrþ×Ê)Æ»§8=A1Ÿñ(¨d…𬀴4@d\p´·ò)ÂoÓñ{àR€UlZÙ×ÏH`¦Ã÷.ªÃg ö=Ñ¿ªeÑÇhʆõ%¦ÜDUš˜ïÆýO aõ…½¡•îïFs“â‘CG£“ùØ.Ø£Ã.úøÓ€Ö03£+ï«w¾g9N?W¤Ïƒ¸HµrsfR> ý®Ùæ“ÿƒ2<.4¦t ­õp2(ã…bc|ûüÍYd¶Wß0.ûƒ0vïƒIóN)håu:’^D­Ö´¡C—ÔrT.}‰ LÂø·¯P'S˜å{p˜¤ñ**5&Æ÷I²¹%@h=MŽfü ÈÞßñ4G0Ò ™aK\¯y¶; @y«îaýuÜ‘¸oÚ4arù-á"6-ZÉ›¼Jœ‹¶1au¬Ã0oÑ Œ”:ŸU ÷•|# »y$­¦ÈSÌ— ÿ?;ý»>±€k½|Më £Â#Ø™L”ȑƀ°Búbkv;ÝÖèg©-ØÒº*%ª *aIdV“;Iôð“§çîÇ÷6ôÌS§}Ñ"al¨,æ±Xíg•{eóì{›ÀH‰w#˜×Xü‡gÜq¼¦µâ@UÝj—â÷¡–ŽºþpT¾(EãÖIJ¥JàrõGë›P°?m–¼†r£æb/}ýžl7l÷ÉU:ññö¿æHʃIyâîä’¯´. ÅáWFÏÃû~íXUq>Œ…Ž]¤ÝpΟÝóBïb¢ÞâRY¨4ãBÌ`«P¤+¯­SLÞ##á!›ä[ñ5y×_ô§²1¨ÿÓ`pÖ a&ÎÖËX71@a·IòüŽânHŽáã4Š,ßî¦?d[Và4ãù˜Ÿùd½¼œ _£¿pe°•ß«Åÿh}ŠFBCº­”jP¶¦½4S+¬¢HÉV\]ÎÁÔ,™z•¶ƒ,23 ¦-œÂ4ÕNý‹ëœÓ‹`8>ƒ¾Ñ¥ŒšQÇ!g‹ØG®û¯tóªYÙoβÔê(ÈÖˆ&EŠ/'O ¬óÒ¹Ýiuéf#°/>KHÜïÙGCÅCß#hZ#CŇX>+<çêgœŽjQ{ ÉŒ…Ÿ„Küºdin²åß<ÆÏòp5˜Ê$ç«ú½eÛÚÏU{ µ½GêÐa¢áº.îÍ3ž‹Ràfòy­ÊžÂHÕ"”ØåWVEÆ;,¤†úWëgíÆ¬®»PªÜµáˆÆ3PJ°ªÖúBäÈï‰@ØÃɺäñ¿!é‰U¥Õj_hsɰ¦?Ò þa@«%TY úe†ÄäNG÷àÓ‘iVŠ~üáq8^)±ÙhŒÕLBŠ'å`“ÃEŸ¨Ñ1ƒ.gå¢Ç)?ÅùQÅM„§hwàû.—`St2±Ô nþ/OÝP$ŒÏ‹„A’â’³¶B\K‹Få+…ñ C²‹†Á´h…ÂÎkÅ>ÿU°ð*¢¼F:Í *ÁÉ}èãé(0Y2aÉžóÔ÷*\[+ª6ïˆs ñ¦ðíÿƒp±PÏq_C¢î‚€ò¤ŸßkƒŽÍvC@É¿¾•9ò¨rçãÿGªæÈëm8d'im¾wg¥LA%'bó G†E÷0vo®2 2fd~²íÚså>2=ÀÀ`eE°`^Ó@‘[})¸Ãâ‹D^¯c©]±/ÂÌc±ýk/ùÜíax‹q.‚äÌC$”Žø±ùç´j&qrÓjú'9ü_…WOî9SY‡Ð†‡ÈgÌO¢ %>M‘ Àå¢UÐK#ôúl7ž"jæzÜï€Òq&]χuÙ¥èq)1?á:ŒuÐ~•ÆQ¢o¥þùÂ(1nÓT70 ^G|â³þü70öPT_9àÜs˜áw/þX+lÝgÕŒ¹„?¥€¶šà ›ù•ËDTF”~ïº^›Sñ“äÈ‚‚“7JDü “oËnnI›*M`ÈÝ-?­{¨TÓÞ:ú¿w®¾–›:;*ñŒ¶ײ‹3Éénºéf70Ѳ ¢OTO’¸"ñt<­¯¼R²9sÆESÔ—lï*Ý3Öˆª·Za(§ºq$Dzs˜p‹¶DXäÜòÆB£´ L­+ }p“ PK>³È¤P†Q› 0’¹D?sC1{º^Ô³œŒfFÈvðhqîÃפàª5Š Zz5¦3T1¼¦IýÚíFÓ4åDŸ¸ê,ÍŸ@ïÑöÑ^ œÉšÔÀ7óTò:“_ÒÔ±7PÍn¦qÏÀ®í<4r#Ò]c ÷ŸÎ˜qlyB½Gís¶w hZ·¹94y‰=í²|Z(—NÚGük3)ªÞ Ýükb¢ ¨ÜÉÕœwY¾àŒA ¡¨ÜEÀÛ…~Õ’Ú %p¡iIü÷ÿý6ãû4ïz­«gr=4jºÀr‹hb›¡'é𛈡r”9P¥ŠÂ´‚izCþ©,¹•d²Ç¤Ÿ€H¿泓åO° ô‚u™P(n‰уëŒ.ÑÀ%’×0`Iƒ¨]+žøëbÑa~$VŸÐǦ±xÝZ¶Nºd/%C´¬Ø–éÿNvz,dŽfx£§”k<*¡¯`Ño)¢VfpìØ5h-n±šÃÉ{%÷·ç¼7ê†ð¶×/h8èq>‰ˆû´øuaGÌoœ¦È4ãfz€‘ŽU¼vãazyÚˆ~¡Çûÿ1.C½å©Î’WcÉf“,òÂD•ù;7 >±ðƒ™z´6˜5 f¶Â¥QŽèey¬ óí÷z·BDvïò~+"{åž$Ëâ’»žu‘Qnuàgâ)™V¼Î˜VÉV¢eÕ*¸ÃÓ¡Ô×ÉbüaãrÒ­@%Æ„e7vÍ »â(ûšéüÜVï*ÉäʇS!Ûó%Ewá ˆ'ç?¼X¿ épЈj§Ò-C‹¿ \Q{¦6N-‚ù€,s›€ {K¡–(> ìÁŒÛüáØD;~Ø?P“ö‚Œ­1(Gíë8M„+†7m Ï}Õ]÷Ûr㪳´7Ý/gð§âé/™Ä·³I)zÆuÒ¢Ù])¹÷-G^û4[©Ú_]§¦åI–å2·”ç¤l‚=Ågž=â‚9[_ÍèÓ¯snµdŠeTòŠäLH]ÍmåÿKÔÿÿÿxÒº­mDÎ=mìZ° gtJ¤”ü¡ŽƒhgÏF»ÄM‚%º°a®¤%wˆŒ‚Zà¦þ‰ }Ôz­ª)çtLºµTÚ倵n›ÙXvÖ“¶ÿlá³ÿü€ÄZÍ⦨iæ×xƒ °D·V 5Ô„®ñB3(¡“áü¥NGÿx£c'žVg–{gÏF»ÄM‚%º°a®¤%wˆŒ‚Žûô’ô!ìMPÓÍ®ñ`‰n¬k© ]â# „áQ?k à2y¶j†žmwˆ0›Ku`Ã]HJï&444t#0p»b‡ïò|áCÎPû£mæŽwˆ0›Ku`Ã]HJï#3Ã-ó-Û[e>ÄY?3Ë‘§0‰†|"[Ý ¾nM†ºED !#À¾œˆüº:„³Â1wÿGŒ-.åû=AI5Ánƒ+ #÷Ky Q¿ˆ\ȯKÌËa²¥ŸMÁÅ0¶`¸~…º¡V ,? Êö]« 2¢ìµœ 8£r¿F¶C–t®’šà®|ÝOê” ÌIÏSÙø0BÌm؉á Å0‚Ьõâ *Ä칌Ž_vÕü-ÌBaNŠâ©G„¿æî4+S5›\¾ûªýÙYgwq?þ¯=ðÆw@‘ööÏîͪ%HÝ”7Я~І—í»Û´]JòMßòÝ•Q«^Þ?ú* x¬ò`EvÐüÂè~Étµ¨D½ÏZ±nXFy‡™ǯ€¿Níß»y_ ›¡™M·Í5›~Ôj!¢’Ærûô 2òµã¥µÎ¢êëà3Ôö w-¸Ý ™®`P¼´ÓÙBÚ[Q-ÝK¬Œ®|ÊM'¸÷RP¥ …ĨÙöB9»ÊÝxê)Ⱥí÷·å^/J³™@>;%·þytÆí ùÚI­ZÀ=<êîPFÞ\; ⸚Gñ+¬Â¼ l?v,—¼Øë#»µaäÐXA‡E×NJ[™ê/€ßé›-ÊNÚŸ©[ââz<åWŒçl„^Y >S2߇7j(‡å4‡>¸À§x:¥`ÅH$0ôÂùàX‰æaH‡=ò'òSÝ‘®˜ çµU XŸ‘½4­kЩ ¾ÿ'ö\ —Τƒö$R‰ ¼ä=Wç¶å_U8P©/¡Wùó4oà(½÷S.Òë—V£8ðÕ‹E©%ÃÑCON´ìáÄýdsëó÷á¤:tŸ€r†ÞÃl(¦[™»R@Ñ˦<¨^á¤ÑŸÕ]Äs ºÌxwÍeÎ[ž#p‰YC"5À ™Ñ:‚ÐJ¦¹ÆìýÐ2úŠMŠLŽ(ýìA£W7$Én<4e6RÖ'šŸú€.8/ü6®#¦xÞ~4ZѦi!`R5¯+¢øKƒZé*9Úv¶A+‘wjýE @­¤$½È® æï­…ýæ!q›þ,£P¢«zC–ù?/Y…:ÔÑîbú¦ òÚRòõl×ÛàÏ(QÙ(ðŹÙ?P•Z’»âOÑF,þ¯s* 7&º·ÁÙvdÑ 1}X µ?ÿvdeÐÊoC¹IþDWð9š%nF»ãè)»›ð\ÓŠÕÙ’¾pÐ#ÉBÛa¼Û09ÿzÛ1ˆ<ÌÑ% cÒ‘üf‰[‘®øú næü4âµvd¯œ5 BxBIÃß!€9?ÈŠþ3D­È×|}7s~ šqZ»2WÎÍß Ö×j£,kÊ©“üˆ¯às4JÜwÇÐSw7๧«³%|᫱?Å MyãÍ‚*ã;ÿ(2˜%è‘Ø½fƒ±€9?ÈŠþ3F B¨¦6¤Â¸r/4âµvd¯œ4IÝçø¤E^ îùD<1DÏ]©¤‚Ð3!<>@Ë 8ÓÃ^E"mve:øš²×¬4#$üvpô@Z²ïëžxßMãw9káºw@+X7 žS9‰Áâ )‚ ýá#ÖΙ9ŽÀÝ å˜ÌYÆeýu-$;äeö“œV®Ì•󆙣>S·/S±LöÉ9þH£4??é8Ñõò¨ë¨·`%É+‘t.£ñöØFöó>5—²ù8°ËyPxyØo¸{?P˜ÄôÌ¢îHLælýáç'´êê:¾r6eˆ‰*=²‡gÕRÀ€íñÓ›R«2ËLèÕT®nϰu½]ó²6ÄG(Ì+w•u"e¡dn­â@XŽ›S¾Ï0ñÀzõ’~;Ÿ‚ 6M£X#¹¿rùa:àš\†=ä–ù´Bÿm>—Àò±‡·uKã@~Бšôi_Ñšíã©Möéµ°nP/}du–Bù¥½wWLMÌáî˜ëšóŠä4Öj8­q>vÌ1ïr>À—ŠŠzŸöÑ<‘¸i5lr1•V´+Y+Ûç¥æ5mÐDëš0Í¢-áde¤ù’¸—ßäýXlÃóx¿qŠûÄœ r(¤«^•'ÆRÐÏFéAÊëÍõ@se òZ-Å×ç  {5€/ؤ+:¿Ø4Š! ¦èFo'“BÃ÷Ý\sS3Fß(Õ=80åGDÜjS5*ŹN=â·@œ¨×òæJÞîÜ0ã?J¼‡TZ“‚ä"þ¬‘T™ ÞSHj8̵[‡:åHø™·Ò£"óBc›Ä=—°åáî„n2»ÙÓìá»­4…OÆ\šÒé7õ«½0ºê½Ë_q¥ö§–ÚÞåûVÉ€D©xŸé&‚U(¥¦»ç4­Ïá‘]T!Þ£G¥@¿}h‡‘{9(2¦…s™ÏÀÈè âZI ¹wsØ÷^?×öx,KïúFÆ“ìOøYX½]9—õÌ-%9t!‹7tÆÝñm¡w‘Òvñz‘ž#Þs:zÃL¢;ÜáÏ]‚ †,n^Õ”íóh’œ+ùh ¿£×§ýHVp¨=R¯ú.Z/ùþ©º¨@åˆR>CxœC sT2‘¦zõ\-˫Γ8L†ñÖ+¼$ȑ뗹åDŒ£„8Kc(¤uY¢¾8°IËîœ{ÜH›°R iÚ+‹åÔüR=‹g—gq³Nõ6Q÷Ój{˜ï䀬€†æ†ɪ b·!5g©‰ószláFÖø¦eüƒ¢¯'€¡jF¹Î-Êž(H‰|ƒ}«”a’]KGQ|ò}¦¢háù¢ É.À1z鬵AnMýä,ÕrúzQAfœ~%†Žc€Í¡e•­!zè®8òUNáǺ ±²½î*]Ãêܯ÷¬(7lÇ©Ò(!”)ýE¡úYÃì ÅöC•[K0!Ð-}Jà=¦Kf²,iwü{3`Ÿÿ,²•¬ýX÷oÇ[`Õ-vÃ’†cÓ&ˆü…¸N¦hyôåþõÁø°'¸š€Y1, ð^„¢y;IwÓwN 7•¯ù¸.³g©bÒ¹~‚Šû«Š V°K©WðÌÍwȽNm™gkÑ_ù„±Ÿ9ƒüwU²@*݇ɀ Ãaü U>Þ¢ìÀÈh`±ú +…7þ®‡¥ÿ}úî_°CF: <ÿ ªÊÒ#™&#£_2€„2ƒ5«û^ü/%™ª=«F0çR(v &_'ÆÓ²¯¼©’ôxÝF¦–1=ÇsPp…K9²[oÆÞ@­Ÿ2LˆÕ,å=¬ÉÆ£÷Ñ»ÉûMN/±‘a°c%Ùc<*&ôl²mݘ wË«‚º}ýó‡\˜ NUDY¯/²oPJ“Ç݉"*M³ì— JÂËa \ªÊIϲ4›úíˆ ë~º¶,Õsð{™¡á(PÝ'Ô”aè®ÚèDfj@›ºhP5mBÙAsåB>Þ+›pjGïÀ¸Y|ñ,ê;PmûV§.?ÔÒNç—c¦E¥Kl—M5c“)5¢€qí{˜p¾xöÉ58H{}G‚C¬ÍÇêsfꃑÒßhj3Ä1)âTãvÈIÚäoÛûÒ@]f:´ßœmVý€…()µ«RÜÈ8@x¯u‰ÿ:ÁüÃL²¨Šd±|â R \¹|vX€+ »6Èóh‹ÌÄünù|xŽ6R•&¢c ª‚¯ô<¬*‘ªH½^5÷ƒÝnØ'•ÐwW!«¦U›×–RøHp;òøOzFdl¹b…Me/•ÿ^Bbãu¦_Ä+Ý¢!#b–ÒfômÞ@p$3Pd÷ËïÓS¸-ÀWÜ"º+°¶oj¬7¬=‰Ùê™Yj©Ì^€@Cl({$4° ïa ìèêQÆŠ …?ÁœéÄC`èV÷>-yP"ü®÷­›'Û†£Ôe⿨ú»,#àþœx»£ô¸=Ï£FÀp ãÜÓ¸(ã©XØo"zÕþ Ö•†±…³¡,?0“5̾¶ÑšÖ`ULúˆVmÓ$Ö‡U”ʾ1×Å8ž²TZ6x:–Ñ;sB¨u¥ r.æ`Â"Ñ–üfçW¿¤Úhéý}Y H±…h3LªÚg7»œ´Æ^¨’6í8±q®ÝE-RÌTÍÜK¹ fNýŽq¹’Ý¿1É“ÚÅ|‹œ/0 plÙ”¼|è$<üœ1¹¿Px“£•¸foÉdIÍ*³X1¿·÷7¯ú®®ËKÇ;M@1‹»ú¢ÿ$ä@aÎu=vÑdû{`jRd°‰ÁJ½°þãAô8ŠÞN\3ݸt›±(úuâ™óaz)×á7Œqר•Á¤þ1W8جïòtÀõV•¸¹ËÜq––=êÖ•a ü€æ0- Ü£"Û˜UÛm«Fýž£àÚAéb•G¶÷A¹RÃò`5üàÁêIÙgŒSGo>¥Èd47Ä=f±“óI>k±†-t)ä­Œb€ëå|½$–ÕÙ†ÿÿVù@øšûšKF~“ö[\ú2Z[ £"˜‡²ÚçÑ’ÒØHë?Y¦!ì¶¹ôd´¶8CÁ¦!ì¶¹ôd´¶6ÆLCÙmsèÉil$h‘1eµÏ£%¥°),(bí V ‰r%hf'=aL¸ÏúçÑ’ÒØF°ˆ1El’DÐíХƆVÈG€‚¹˜KÎâG6FY̺$-F)R341åd+Yû°¯'²ëÒqÆ%( íc tõÞ-‘Ú÷HŒ´ d~Ö§º}­V¥œÖf±ÎeÄ•¡ù¤"á%Ôÿ/„Þ'“qò²Ä€tÄÈ_uPèú9·÷ ÆlÝX¯ jq%J;gJ×ÅflY±-FÀFk¾JB_TŒEÐÎÁ©øÍ“$?›þFS|ÿ'›ë ¢J¥|"õ)$Óñù$ ¨Žàf5É®Á9……|õ%40 œW ¸ËÌÖR)^R8½;tö@ݤ¬(SEã\8ÇŸÊ”P†%(9my| óCæ:Ã踉 ßÃY‘Ò$#¸ÆcI® ’Õ7[oõ)w‹< +U¦±§ê†­mTáÏev%Ài#ñ eÏNEÆÀ`ëRƒ5<3$ §qTK›Ë3K—ù7ôN°ÉOEò­^&ÝlW4 ÜóÞ}êhŠcQûÄŠ«ÏPßI”ËËrW dE ¿Q}¸ÌmSØ7V~‚÷­²Í¶ìÔNˆö”˜|f²ü£€«h»†µ* G«Ù†ê4ÞIlp¼ª] '‘¢Ùˆx;›˜ÜFÔ>óîòCëûÞr§â]jˆÊÖ=‰L½/9‡âd›šDáX3­ ˆY×ñFÄoD‘{R°¡øÒ‚:Aו(¦/×Zݮ⠈8(P¼ž Éð*ÎУ$d‚•¤eÏ2˜ìÿ6ã)p,–Œ± vñ½©œåC ¢Aø©ë¼Viz·ÓÐÒßý6A⸠0íG”êRQF_ËVsL4Cö,Pøèfú*(2˜¤QÁ¥½“¼RžÜÖºŠ0+lD@KÕNî\¤'rü˯“ÕrÆ;›Y‹J µ söJ@‰$8ìßNÃè?]õ yCFK|Ž#‰ÐTñnr /Ï–Ï/yô¡Ÿû–Uý^W §æ~ /ÜÛ'ëŠGÍ5ÉÊ©9²ØîR ô݇LVs¥ß3.qÉœ;’J#Ùc Çöµ`CW˜Ð‡1Ñ"ñHC1f.Ôj“¾Û‚Qö5Ú#à0}uÝÁSµ`|ŸÜÚ³ÚŒ/ãvê\3ú‹¦,½—·ÚÞ@¿Ë©8ÿ:˜®én½‰¹MñÂ9ÿ@™ó¾ä½39o?>Ö胯œ~÷ºÕÍæzžXÄ€õIG0ì˜&çjtk}|ÓP: 7dÃ\ï7Òy’¯¹#3ÀU‘pxÃwó½’BÚÈyL¹T»™¤x0¶¤a`áüø>|Q=F»É¢.K3[¹§2TqT_‰t‡LžŸ°€‹²£ñ=Ïûééæï¸¸"ª±X³êm"ŠaêÖ?ç;›„«äWI-Ã.O–ž¢#%½ÊOÞîê[cm>VöÙ[MòÊØÛKß±¶‘pÖÆ½êf_F»±mo‘-d†²ùŒ„Úëc¸ñ}"/¦[*‡~# DÞŸ,GjŽ’Ó«@pYräÙæI—î–ÁÆθ ñQ–¹¼ô]†Ã` ¬»ñÕÀô6Ÿ)§¼¹)laMx®]>ƽñ‚mň_é6 xFyà*@'o‹‚ ;ò°{!3Ò¸@³&-øðZ[ux|#©u¹L‚Ûâ´ÂûÃu)d"uL5ºeU+cŸ6½;l.Ãýgb??ò  =À^ñˆq¡sš!ãp³Ê’UÐÙÝÅéØÉsñÜkÆ#±Ë+÷ fSE¨1tt¡%/“–1;Â(4Ÿl§Ä\îV[E&lîdœŒr$ž£¦s’hƒèÅóTåt¡«ÅosV¤nk·ÃØŸTËk°9Æ—…sFHü qê¯Þ;kÙÓzàPè˜6ÖC× ­ªÊÉ:¿³ÏEX𺹀† ´dTàGu v §T”éÚYŽ÷ªÎ3³-Øøóœ-•2“ÞòŒ¡ªek¤ˆºR§®›P(A¶i$‘‹NÄòŒz¹aùïßš~‡¸€=+ G×6mHˆÂn8ŽÃä?àöjªòÁo;‹F;E;i‰"9µ=£­æóÿ'àä 1Ì?Û…¦O“îNëǯ´¡³±ÉËvc*4xß8) šƒ=,§Œ¯(eÆ·ô΂wYÅj:ùôÚÅ ADÿiŽ¢Ñ>6RMcÌ,œð ø×N€%|œPöOñµ4Ê·ñ¤gäΠ-×åñSž ûÄ=êÇ¡ŠwoÒUÅmvrçàˆ»d,þ,ªtÌK¢Â²£–EΜJ ®~ Ý÷ 2v¾©Q¦Ëöß>äO¤Ù'eºC? Ýñ„½x$ù$#;“/Š õlb¹Ïyº§eÙ»½r—c1¬,—â¼Ñ>:î§g1^æ)å0æHúÖÒ²œ!Š"£AÄ®Öðõ’Õ+lê8]¬W}À7¶¬0µ6uì©Ê‘J~cW™ù îˆo«ÝtýÍê0E ¸Ÿ8´ð§Ñ诧QgAB fßéþÅõ§÷œîÚV59a0B÷ù®SŸ|yMªQÉÕ„\RÛ(´¥•&ù,æ,ˆ£rJNje1iˆ®vOºñ‹õË{¼cy¥ ¢sZd+üg®L‹oœùR—°mÝ÷Ý“Ü|¼l'P5Eº­ðÅåx&BŠ T0ÞA´±žƒ›ÞËs¡ÅãQóm²]µQ‚¨M[[E‚&Ó(ZWëôˆô< áãäek·F†WÉí~¸•©6>ƒ®noïIso¸ô?½0³yÿpsq]ô ©Ø.ócÉÅd"òeð÷h<Öøð}¦1M l‘è!¼UBâåS ; ¡‹Òõçà:PžJvgcV€qŸÿd_T?±?%½—x+²8;ÿ~™Ô“â~å®îè“›%ø‰V¾g­ Cÿù²ÌœŸ‘…-Àn7ªÂ–ñ¿?†  sÅÿaú¾ùmZL³d0Qõ`V“¡Ój~²c–Öa™DO¨[U”ƸŸ¬˜åµ˜f`Qêm’à0ÛÆY)Ë3lðŸÖôª¬á‚!¶IªpÆts`=¸Ä*j z%Ÿ±IMHÚÆÀ,0A%†„Õ\ž›¦{ðÒ «MçLŒ0‹]5Aë÷þº‹X/{.óÉ8©ÀáÕ¶Ëê֯ߣˆí뙚ýÈaKŠÆÀ* 2*–¼¨œñ†´Ê·.Šü™ÜƒB·~”òGá…,ßòKš}€<¹âtýÀ|'¿¦#Κ¼ 9˜Ï\’Bö#'¥mlw÷€k#‚ÿw@LÜkMëyk¡èŠ"¥g%`“¶êÚæ¦ûY¬ >ïÆL”<˜ –=¬ª@ymk‹ùË®è‘&@þ'-C+µD~ww‘†CÅ™³¼› qþSè|ÇWšµEîS‰vÖÈ^Ñs ð›f“gL»°ü2ÞðKN«Íäž|,JRœ)±ÓÔ›kµ8:MWç|¼Ÿ¶4µz…äìOB7) 1ìæ·0±ÕN뾨û’,÷„`b,-¥ªy,°_F˼W‰,±0\_’ŘYã{ÔURyžu½_âÁˆÄgÄ—€™}O°Ç•·°=>#“áö›QöÚØí‚êû8 Î[AÊH@²Ò¥Ò>8lÀQt¸ÎgzðþŽÛ‡G5ôÞÄ«€!Q…j»Õa¹}xR!|“¦¾mïgƒ5ƒ¡ÃmÐ^<&ÐÚ‡Úµ0ŸVB——“íy3ýÒ¬ÓG-— ®7þ9QÍzEïö‚ΦásÆèØÇI—ûÐUj ©zf2ú›-òÀÎ+OA:Ž>ŒÊµf² щV5V°eàv,^Ç÷冚]ªº,ŠgÖ@ B"z‡u—°Ó+}rÖ•ËÚ«™P™ÑV“$»4‹Ãdð93£Y6ËË=>{²äW(ÑBD%'ÔpLäÊ–߇ù§D­®Ë~ÕãN|î>,iëoÏî»Üt]ʃ,4IùPfàm9/±@ÃX1?)@ÙƒËâã“)Œ>t}‚”{(¦¾5—žÁ„?|#"âž ø ”A&„³—g-¯›ûPzt BX ú¦ò_åéÜ»o“XJ¨ âô»™Âª¤êùqã‚1¼² 151K^ÐZÊ%}{ £ñâÙóŸOæ=³Z”„U† ïœ¶£¡lnGˆg­Ô•d1®Ñà24BuLÂ゚èãÐè(Ÿ·@‘…kEõ1ªQêS4Òµ¼>êXÁÖ8X²¤¬fkÏf(šéiœ"tW†GŸ­ -+_Á«É¯§Ìû"1å÷ãlžb×ËÈ„—.´’³äMA¯ü-&~D-’laCâ4`(Äñ;Û—¦_ãW•ûžžnô•ù£è±–Öö×ÄPp¬¬£H†×3«[PÝШoK¼WíÝWݪ+Tò- ã’g…"ƒ{ÄÁ³lK%ûÙJ¥6Ás| q—’Ìî§KV-Ú¯˜Z¤Ò]œ¸;Œâ7`«·ê¹Î%O®°§¢àYMCð‰úenà»ãvÀ«ù>‘›&zDèIÊÊÚÔº¾ýC%¤]§òû&®/WpK* Ði7Ë`מˆÀØúš­$F°†$‚g†­a0J JIŸï’ᕽ±0dX%*ÔÒg—ß•Q˜@"¸wÑ–£’¼ôO–XÿÿôxIc¦n>YVˆ ä(Jµøˆ ´ðiV¦€V֯ࢱRŠ,ܺI:°f,>Xr-Mû„"ø›ôyÖÏø –} ð)íø=ŠK4Ű¡úþÃD—húH<|6"°å*Zš'÷Eñ7èó­Ÿð,ú@à>SÛð{–i‹aQ5æ¬Qøwë°]Gœ í¦¤üDŽÛüyäÞ¥_ñÈåßÕ`Æi+æëGµß„Dî¥'d:Pí—‡›fÈ´ˆº‰~ÀA ÇÄ0ù†[;ÁÏ)® •mêôT 2îD p°“]j'†©ªêY8ŽáCOa-n°ì8CJCá‡!’Sç턚„ßKÁ‰ #˜Ã§+F8#ÏËØ OrÒ3-~†ß|ø¬¸‡TU¯q,(Ö³ó¤@Ð:Ïȶ9ë wð{í 0¯©Üô•®DŸ)à¾V±Öé‰ýl)^s€x•‘”«WT’‘|:|8|¡obŠnÓÚÂeQW ôJÁíêy@(Vˆ‚Þ··Ý Ý…,œÃT¿ ÇPN8%œé#üTÍm†íàb_|ÕbÑçreAÛ~èŒifYMM,©.)O O‰Jî–ü+ Ýb)öóuêç+Rpúô³ž™>r¦?™×ŸPQ"€Mµõs¶í'Oex…IŸÒ±z*m3õ2€\†¯úªhj9)(!„²ÀIª}JµÄÞFÕš­:àhÖ‰P’̾vù6‚ªX÷ñOŒùåL#fËáàÚ«©uðuÜÆ¯üä7(É—'EÝÌœkïÂΨ€$øòàWHšã^K®¡È$ô>Qý ^u*öºC’(K,·¥’…îé2LhùÈØ…}ç*Þ£cB\L¿ÌºLÿf=e¸ôu˜¼Í#­3˜1WÌ|ù9+6mç ÁÈaª×à5Âo-:f”ãðü`Ç; >ÑôÔ´ö(FÙ¶†Ò6nõ`v¨œ&&ù,äº:=Ô×ÔLõù‚#Djìí¶M{Ù> ªYFZDÎXˆ¹ ÓD¶eðĸfXõ?^Xí&¯˜éãƒ^XzŠƒ‚ƒ =²av÷;ns`»Ñ¤ªªDf¾5µ¼¤§¿›’¿7áŒrèÆŒ»TÂᦳ_³ {¯g€É~©ÞÊÏÔ ZûIå~šñ'ÓL¢èB0©-ÙX±$³Ö˜¤fðа՜[™ŸÎKSŠ·Á6yšåDÙ·óÀ2§Æu5ÏÛ–$ú¾>ËëÒ{±Õl¶OØÐ¸XÑd‰Þ¤šY%ÿ{èt\‚êµ U‡î0TGíyõ•T¡2Qjeöwà˜p×qØ ñÿluLe72®KÛG~ˆ­µV–©ýFo:„0šÇh‘¢‡ ×)ó»ãÆ>¿û9\ë§â;ª% ¸K¸ýÈÑ¿ ‡e6ýßÁk©×IÊiÐM ^‚;Ur8fRœ›=>…|n2›f[~áòt¶h`$-rR¦Vz­K3]ÞÐP33õW$ðb@ß ‡£â³ â3†çËõ”lj§¼V~7Â!aÒD}žRÄnYà 4™pLúL£ïIê•ä !» Ùm°{•c¬ªöWã;è4‚J-k+o~Ö„ø”›Ç_ Øžï3 ´˜ð¹F¬6Éi%cÆcâ-€œ˜Ñ†šP%æÍ–,ZæcUc4ޣ⌷Õ3éj7’¦§›a¸{%S-ªhÞrú€žêx"v´1¦øxH=öñÉX–ç‰Á‹?²ÒÖPIô|™®.¤«Èj°‡Î¾´€?zÎp1¬—Êhf˜f—ÆØoh*Ä‹QJ¥4"6užÒQW/ µ×èñüQHÛlp×k]Õí+=Kõiõÿ ™™Ã]X&ŒçWžÖwUÖ[ÅøÇ9MòÛ\.c­äç, ÿÿÖÈ¢‘¿B/á+—é<éÿDBK†™9Íbýš¦jBE œ/\4ÉÎkì„е3R&ÚCƒ®dç5‹öBhZ™© ‹ƒ®dç5‹öBhZ™© aF\4ÉÎkì„е3RŒz É ú©»ïø©r'bIUÉ«¦²sšÅû!‚˜3xY}¼Ž$›z%¿Æˆ¼´°Î壾ËAטÄb”Z&T'ˆ~ø ]ê>Yìö[@ž…¬øØ0ž‹“\@ñg²Ö×ڪ蒑ÉêmYf¶õ!9„”YNùÀìmš$Z¯„¤b0ýÌŒD¨š½÷ä¬ 5Ž‚M(¾ãK“1~ZŸhÆmÏ<Õ©òUΕ< ŽY¾Õše’è²»}-ÙÏÁĸ#š:â†ðXb¿ö]Ý[öÝ<±öîvëê/gP(CVì#+)‡+f«öŠP@¸ï‡¡¡Ž•#QÜÀ*²¿çž·cº¡0 Û+œ_fµh®áBßaqau.Glí¸Þ"DÛîKn†Á±Þr]&”->ŸR\5<ŠN'-xΗ¿ð¶Ll\îéÐy*S”¦W²ñL¹ªšR=ûu¡ôTü:6ãbWÞDö ‡1 +j ð~Š@àô¹Ë êñ޼çÈÚeH~FÇ&31oS…§£!©=•“׊XíFtÄAr¹šXÃÔah¬%ëÞËs¡iJâèçºÊ8ÐÓ)ØÁN®>œEâ/ΉiJ1I`¼;dÚÖ»ø}&F„êQ°†yû×1艵s_<5<»£]úÉÚs"H\’ïâû?­È "ñã}ýŠ;Û)Jô’EörÔžyTI”iýú)cðyù®g Ìt,§t_ ¨ÈËŸk0»‘¢áL?µšÛ0†7²í®XÐÃeçäÜ—Í3³IfáÙ1i&aþ)mm{Îõjå®Ü/Š£)Qz^ÈßyÓE÷a+’Ïâß½°,cçZJ⾬’™‘E–ƒûø§\}„/Ï»ßT{NŒj×5ôÕQn®wÐØË×ÜœW$¢—»J„'Fžšp‘LN¢å`£üu»KkÖ†p†¾*«Ÿ`$M˜ƒŠÙK²ÝÍ!†Ô‹)›ïdõ–/p@óLÝq¦%(éQ3ß')¾í|X‚sÍéSX>q"!¿NìW&«{ÜOdò Ü~FÅÕ?Еp® ô,H%nŒ {œ‡ÿ™£ðLÞ[ÒZŒ¾ÌÕª8u1JëjÛub/Ü5(óð¸³WS{™w˜Ý“É?f)á|:­ÃëÎöjÀØ´¯þñ¥<ÐX¢q÷9gIURM™J7c¶ä…=8‚&]‰ÕëÏI©*2¢=¬tHÎBˆ»üˆ¨xÛ"‰… qzZ,á$)3ÁOÚÜw¸4|ÝË& W|#íŬÜKòzs,ÙûÂÆü%Ã.ª}R¨4Àç½zRÀ;Í,a{Jy̾ Bct}Ó­žôŒ[»é¨·j.‰Ç«R\Ì.Ò2ˆfcÆ+V Béuç&o›ÖlŠ7“òj¿XI{{3±õ»~UÀ!š‚ìZ'¼ñ=Õk‚‡8 íD={i’øt ë\»Ðâ¼Ù7”ôö†P £6üæô­ìNfŸW¸+²´i1˜yßÁ¢Vpö£ Æ#$ÇtnÜvJCDº^L¼ºÉz[Ïï »™ ÄùB°á£bJt÷v”7êŒÎà­\Lâ†è·q(OÖ±Û+üÔ¦ñî‰$ÚiÁªykæ¦ûIeêÄ<#ÑI¢6ðª ybwÕwçáÑ´H9s i‡} ÅDO°Ð5.{Að-ûÙƒ‰ÑD'Íà[ß}iPø"ê¸9ÐäÆÿ0˜™ºÇÿP’kK ¼Qµk°S;P>g•ßµøŸš€ )Úó<®ý’I @í@ùžW~¥…Í@í@ùžW}ÙAtÙ¨¨3Êï‹ðúfǰ*SázÿƒP õ†R»IS6LkD›xª' úX­7Ã]rƨqÛIù¬)jŒÂçÆ…×ðRúd÷™ß‘ÑõÑB×~ŠJwލ&ÁÿF‚Ìt\ßÿ9^06.#¸D Anª§Yú^%l2*—¨®8RÐ’|F'‹^¦–œr9VºzÙ ÜÅÙÜôz,š’XªÕ"ÆQ8é·œËQú<É °:½¹ÕÝãakaç‘ô-γ¥[6³–l=óõs@5ñ£×Šp¨-‰Ñ4pk‘ }tö5aÙçÌz †g¦ÜŽ&ßUÞ u\ßÙñEÌæ4 £’FÔÔ4À¤@°¸#l)ö".¼·9´³®y0”¸…¶ó=B0Fêî“/ùD0ϸ PkÏÂévr¥¦õ*Ç|m¾¬jçR³®ÇžËWÁ!‹¬çªÍÜ-ß±öàÏ4–ÉAlLÖ÷âÅéÂ¥Õ}L/K„PB|qÇ2«ÊS%ì`©!¼ß{ÝÑÉ8·¤ævZJþ¯X¼@çÊMÖñ®Ø¹)€O´«4»“›|£feôÐÅÔÍF¾Ô6ǘ£ÅÇŸÄmÑÕpÛ0ÉÈΗïhŒ­un7é>Ä•€ï~s7^šûÔÜ-SøFEÑØãd 8c[i쑱¿JÎB+‰g0%v’¡ nJž-$Ÿ$EÛ%º Ÿ·‹r)"Î…kî&Yc©þlªÔ0DæŒ õÒ-Ðú°™Ìý`XÅCljº9çmCOÓž†ì’¤ß½âe»Jz/µÒê/ UÂ(9îíà-•ò"îcf‚™mZBµf–TÌìðeÁ3Yoþm÷;¿¬ûAS„Ç÷)òE=R=5–7V"’Ȳð,ä¬UªŒæ¬©I.³¼î8›‹6ýW[\Œk­Åy@Uì„èèf•ÕfÚíöËçAï̺ܲÆ0>ƒJ¯ªZ¡~é§ŠEsX.d¡ €¶^g}d”·™iw–”²šŠKù=‹Wª¢Z paÂ-?òsƒp-ý{!aHÇ#÷3ŠŒT P—¿·€áóéCO«VÂ8m¨°L5XÚ¾¨ áM4HÒ×y+¶#^¢+ˆL7ޤqÎãt™2@äRºìÆ$îEw°ã¯k`”˜”O¯+Ñ¡~Áp’Ó”Õùù¥ÊøÉHâÉËpÛÞÈ”Öø˜›Â ¤ €ÉéQÓ»|Â; æm`.»¨Á4u|A6©QÜáHIÝu¢Y]W§ÒõVI¬+©ŠnN('ˆäÌë¢ÊÖUp9A.nwo]FÌMc=qÎsDc†âVl‹Ú %›.03DÀÍ#â\Ÿo ’Œc°€“¼¯†Wò¡ºüÒež…Mûİó×d(O†´kˆ%nâ¸[~¬½aê×—1ÞV9=néé‚—»E~šO_jü M[ÿ!²Äd@õº‰Ù’vÝ7²gLq¥: 2&KeˆìÿŸ÷ÆÊ¯u"FŠj $^cŽ­ê ËÑZ³´ˆ¿?À}¹dÀ“ùÍ òŸÉó:_͆(¸<Ê‚V›…G€Eð*~¨•- ·Õtt¾@š™Š×®äÕœØÄ(²â€#š~’Uv¥m\©N )9¸éSÝ‘ŠIË:šŽ= 0Ϋ¹z#@×t6˜_FYÈôÂãÎéë‹ÊòR%ÑÖõûäåýIKOf%o›I]Ö¿#†Sœ ·Åi»WHRد•x….]ÊÈwü눯QOÚ3²§ Yè‚ÌÊÎ+ 푉b„n¦†^U¢ —»6©Ž©¢È†ûþbtÏYª*zí[_С¹Örëdƒ\BàrÅáâ'ëoRÕæ¤T݉™ŸLzû< ÁšoÂ?ÿÿÿ~Ž'ßãöÞΤtèŒÇl°ð}C§;üÿ9ÄaÙ±+„t©JØæ{•–ÔÚØž²‚Õÿÿ}?ëЕ$n±*·Ãn V©žÂE'þ¸/Ê–9ËMç·Œósíî×`çèþ¾N²osu‰á¸4yXX:·»ýp\ördp§X'âJÔ†´uqê¨ZW}F{4wN_3X‘±.ƒG•€ƒ«{¿×Ïg&G u‚~$­HñшkGWª…¥p`hb>ì@ˆk‚ G®‹¡oùÞá#£ÜåB•~Fëyj1r;Ó[ò°‘Ñ#ÃbïÊa :þG$pŽz‚ÂÍs"µÎ õ3F©d{gçÏ‚sÊuÓx),µy\M‘ hǯøHv+êXº#ö+d(ôˆý‘í[ í€I†’XALnÓ:G~td|173óóñ¹J•çÙõ°x…ÊhãÏ‚—㙿- Îþ_`º‘´¯Í5šg>Ô1¶ˆ,øËR7µk2%Ìó$•f4Bú÷Ÿ^¶ª¼EÀ¯<ñ¦J¶²¶â>"±…eËQešågkü¿gÅÄm†VnýÈmI-µN[KÛ8Ÿëæb»ï=—¡Œ<ˆÙCS¤ xU i Iš¬{ɶ©QrŽ·ÿ†ù/R0oØÖ=–Àõ|™vǤ¤!ÖÙ–FFù+°jGÁ}ðÁ•–›ˆ{üÅøð:Ø21_:–~ÏŒ+¬³áp¼»ëa8£›Œï„nÿDDúý­ ¬ ö^ÖÑÛoª mA¢ÝûB_hÆO}~…ÖÆ‡2[†~M›|üµë<€Ds<ÔWE‹O¶Y?,~ö%½F¼X²$wjÉü^`K]á¢ñÿ{3ùg€ô¦֨@Ä2ÏŽÝAPÞÀ€á¬q➸ñËš"QIu-–­Ä¡Ç<0ô7˜Ât²š]zUø:]QwB™R&}q¬œQÌMV@7¨Ú‚`j+~ëùÒ¶¶˜(§{õŒí îžUÒû´'Jгھw€"” ­–NÑ·ßȦ,U¡«lEÜÒµÔP&¯|âê8ä*w1lDÑ/Æñ»61v/|„Bï:“-’tMz‰© €ó€ 2×§¼cí5ªÄƒë©®¸ÝTnTB¡á¼”Xe?ÿy>Q˜¤@í=! á³õ[ÜÂFTºÙ¦Bá¹£üî„a9ŒUâm“X !Ž˜T*±¿€¾gkx"ë‡cnmüPÂH>óôE&‹ Ê:½u@¬.^y,®)’µrOMRxÚ¢N^!3ê£5¯ø4ÕoÌ‡âø‡lÖž{ߨ CÎäÍiœ£/V€Æbʳï‚ÊÆ&dWä*ÍCÿ6‘ŸùÅ^(¥É~ñÙ—I£*hË•³VQÝ„¼‡po#wëúœûЏ`2,×cÚªdaÓ)çåýpÕ}{¤ÃHIMm&ÑÕD\ÃÞ¸ŸûC+ø¸ÒN€+bò<Çâ¿s­­)Ñî)êZa=Ô* JŸ‘4¯ÐÔçÇhïe<éÎ|×í7óg ÷“Y¾4!ý@TÙÎ<Ï6ŠðŠÉJìîe~iÒÐCrœ†šªî^išUÿ‡¬ìQWèdYíÎ~†âñ&è­ÛSM°Çãôú”œíüíXŸ ¯ÄÜöÆ3&¢­²Š˜é…pÔfÚW´xâäó Jô°ô\²Ö£Eœ|.ÎóÜ{íW° ƒÎöÊ—’(ç¹#ü`ԺęHce$B"fžjøµœô½»Mü=®à\›…:økT2rˆ‘bø6Æù–êK5˜²GWf†K>¶ݥʧ¼ìÞøå08û?&ÌКçó6ø„ÎoëÅŒ¹™q‰ªF ó?FžhGš>ÚúÔ~ÔúŽ_4ëÑßágÁxÐ þ&‚À9ÿOx72pyø• Á‚Í4hmEßî¦~\Ȫ»zRÏ>c´Ç !ùÉùó&ØtãÌSº«ÆúôØ  _Ǻæ›?켉ôŒ Çœàö'Jž1;ÎŒ`üó}ÿ>šQK›ô á^õ;ªÊ.¼oÞ³Á_a …᧶爘Ðý%›d3VÀ|öÆqº£‡Çõƒê—éd¨¡Õé-즹‰–äPï@wºVTL…¦HĈPqß,üŽ=/&wÁXX øLŽ”ÒÉ¿¤šÁ\¦ýXmǸ õhRø–\“¯DÆfEô0¥¾ÜÕàŽãXfù¦¿1‘ \ñáTP‡»ŸdW&¦Óçº!7ž¦æÀ>kË/W"ÓBŸnT  ˶16BñJ&a‹Ç•”z¶Ú{ÿÿÿt URq!‰$u4x÷aKLıZ-ÛÉBI¾þü^FŒ'­'«%÷;¬¢ÀÁÖÚôî[fE‘’÷A‚»ƒXI=Íήt‰…ÙreS·«Ç 9ÜØRJ@ç6­X«n8>ïc,hÂzÑ2qZ²_s°zÊ,ì`]¯Nå´"¤PKÅZòç=Xs¤`ÿÿj›‘–4a=h™8­Y/¹Ø=ev°.×§rc\Š uÑ$aøßc7žàßÄõ¢dâµd¾ç`õ”XØ:À»^È®tAÄ0I všß{ÎÁ$ÜßÄõ¢dâµd¾ç`õ”XØ:À»^È®tAÄ0I všÝ['ksÖ‰“ŠÕ’ûƒÖQ``ëízw"ࢴ† l¨k.U‡ÖB¥ðfz/’?è7wñ=h™8­Y/¹Ø=ev°.×§rá(é@-r¯“{Š”¢¾‚vQmžj+A+ÛÁ¤ˆ ¬‚0c¯¢P­ÉZ÷³tddãß<ß÷ ±ëHw6ØnÓ° ËE÷Ͷƻæ®Bb]»Å…*ÈÓú)•y¶]úªD·Ñ>Q«Øß”UQw:3Mûq‚GÛöð9Љ ¸Š…6Q_Ù»a6ˆ™Ø¢ð£B¯ÉO>÷F`þ‘17ÏZ†ÖÿÞ·–]Õb¾TÆöd¤p[tVíÝ\A8-OÇ¡˜ŠqãÄÃKXa_w§0‹)h‚âïOKõDw–øíÏøÚ/ö@¸d`š™E7 ëþ6¡GÿQÎÞL‡O`Ø“/Ìg“S’/ø… ƒÀ¢—ðÖaÒAjq…>“¯HVðåû9Cÿ6ê"á0r}4Ï™q#ÀÇWpÔTõ; ¾—u>¢éndræ®™%9!GÛ`ê7y¼“ŠÛ³—†És<5'Q€|Z4IGÖò·ÇÔ[¢LßýÄ}öy tÙ6!bÖ(œ¶^h“AQƒ³0X…ñ‹ƒµ¡hÞaŸ+!¹ÎÝoÈääV'dˆpu=°v¦Tªbª´o$ÒúOÄjíâÝD×s0ßò“3®É©òNç·d–6‹HqNwr‰†WÂ]|5æ4¾V7Ý=|ÙT»ÜÜÚן»·ò ‚ýÍ8ÓpcT1ÃF-ÆÆò‘†:T§EYúÓÍ3ˆVÆÿGsZônG*©:Î„Žž9Û7Å,Ø´™aÿKh@Ü+”X÷3uw 7m4ŒYäxìu”b…‡B¿TÉËj¨ìÜ%q´Ÿ­HŠŠóÔò eˆ¾|Ï1‘>-”Qéÿ1姯R©s9϶HîtÈ1±”®`»4â‡r~¤±åèZcj“á>¹è ÈÇgà™?Ärø¾!LŽéÛ»{ÚB‰â_øI2Ènú—¯P«'_ûÿ)Å~­Õ&F_ŒÞQEó£oL3™+I#ÿ^2ƒý{Ž (Àþ™oò7lDCž¹ÑÜ›–lÿiòù¦/pÃÉ{‡.Ô‡£ÉôhG:m½<3†¥¶-Z hšá€ƒ.€‘o’Ë{Gê9E¢§êÍÆÝn°¯£SÓC&¬þð)’,RáN•®¨Ài'ÚçS÷±'m?Ì@ä—¡I|©fýåšÉ"x)ÞÍ+,žþ»1ûâôNÙIàÎ6›)x‡DûËÕ7:ö,§ßˆ¾É 1 /DÏoƒ\M¯« F–VŸ¨>ÕÓóW°IÀðß“XûÇÂ¥ÛKßU…Ò˺蜒.Ñîtvšq²0¾YÙ¿KÃ×1D~ú"»Áš’Ù ”Æ+?Wx¢û9¤õ×VÞ¥uÙªT5(]üWy}¥`Ð:&žmcdöFÂç]—ü8H þQbgãJcn¶õ#Öï Þ:n§Ôò°å4«ÓÞš®Oh`]˜ Q‘ôÛë‚Ce„@3 )?­ÀGa:/É«ldGLGP¯‘wRâHúR=̃úõeÂYoÀŸQ5»dY/L!2¤žunå’<˜éU~ɱ@§°¥éÉQÛÉz¢YœÝÌn©J¤ôk¢¡'¸k²÷Ó ÃaúE'ß5¦8¡ö·ÏKÓ‡;*8ùZv²%¡ y•$ðëw,‘äÇJ«öMŠ=…/NJŽÞKÕÈ4ç)¶A1QÇQ» ’ ÿÿtSÃNÖD´!2¤žunå’<˜éU~ɱ@§°¥éÉQÛÉz¢Yœç/ü‘ÿíSb—äÐÇ™RO :·rÉLtª¿dØ SØRôä¨íä½Q,ƒNsH¦S¼—7ÓK²ó*Iá‡VîY#ÉŽ•Wì› { ^œ•¼—ª%iÎnjÊ@fgÄßM,ÈÌ©'†[¹d&:U_²lP)ì)zrTvò^¨–A§8ÐSû‚IL‹ín ºìÌúG¶)~M yŠƒÎ {–Hòc¥UÊËS÷æ¥éÉQÛÉz¢Yœâ}£ÝO%ƒµé`0F§Ã "ÚM|¢…®ï1 Ó~Ý7Π‰ Fs^³°uÄC™Éâ–¼ -) 8Û~™Ø†9¶(–nÞH´É"T?IÆu¡|ǃèJå2~Rcûúë÷–µX Œ}ü¹¤7Ïå¬ld#‚ã©÷K@ßàÈËÈŸýÑ"ùÆ6þ ç@ÃC§.Õ–ŽÛ£ìöñÂÅ1ßéá×iÆO©¥µ?CvØÏSÑÉ/`ꨕý×dwÆìÊXPƒImEGãNøIf„‚õ)©FÔ€}'þ}æ–R6–*Í­Ÿ´m§Ü²t=œBI|ää;3ÈN°LEº—Mï)]S0É“Œ,ÞïT¾Ìô+ ‹Š<’wDÿt÷`ý†Å"¬mQ²Á,øÐJ§@4ONpÚó1AÊ! è°5uxv‰ŒFÐrÉÿ^ÈBú¦h2ÝFQö;ã>—ùŽNwmà€‡¡¾C©ø¦'kMúúüE)™A€µCy•ñ¹hY#΋ÐîÂÚgkn_‚Cõ‹ƒBùcFbú9ö2kî,µf·Íy´¹Ècga—Ç;Àòêþx”XàÔÕ KM¥—òÞ(²ã»YqxhȇÁѨ1(†h³§gPº¡Ä¤ö6Ç‘³õ†"këƒâç\{"6Þ‡òIæ\èO„GEøÓæÁ ¢º†8v³ &WæbMs ñT%.4uîr‰^g55)È¢ ï¶)iG½¾Ã(ò¢püÞ€ðÝ;ÞÁ¾Ñ"4íÖûá¨Â6üMa,GYœ Ú¸µi`¯GN&>Ñ$ûÈÅèãõùHê(áel[H‘+øà”Sß!*•·­¨+"ˆ¤tKåp xÔIa™ ŽAL_oјí%€eáß³Ûö‹8gãû  ©ü,ü:GO) ´PúØ…’¶¤âÅJmþÜAe„IáGÛ§“ª˪›±3Á•Ô__”j'øh\‰\§›l?\MbEÅXnÏsSB}:–§8Y!ýRƒ¼æ‡ÛÉBº7u JÕxù§Xè¡vX(ƒPüüÜïõÁÇR7Ž=…ED¢%F ûu¶؇j¸ŠsD÷es WLòôB™Q8TÆ'Ày8”5Vg£Q™†L À†>BH'EªW꜂ïGq&¥l”˜$-/âÉ^ëØŠ€Î½—JQäJr4fš=òЫJ€.bìz¸Ø°gáX‰çªèOlø¯0Q¤šnMö$°1ÚwóØo;05%ÖIÈÆ¸ }Ä dxÊß‘®q HŸ› ÚLŒR'¢ŠÝÑÞ@Pp£ Xñ—q° ;†XÚ°­ç¶D‹²)ım˜Z+ZZ*úÓ µ˜ª4s_ÈÓĽ&tö(BíUÀæD}––§yØ÷Î`í’8&–z€ä ?C62Í“·"—ÆxSB‹ÇÔ*o0£ñC½Œ!î:9`˜«åà*›&Ç|sõÁÊn§F†5”šÙØ4.¿<þ?r\ÅqК“i¢¢ºnT¯NÞ¦ ! ôñ›\BŒ†úÅh¡œ 1?3ȑՕØiÌÄ]÷t>άÍ~jÜ^ ¶Kê‚ÊB±ú:¥™Rå6×Õõfì‚åøãÁãv Zdµ–;ê=ç•”.Vºœˆà°M}»Y4Èd¤¸ìرèdRæH7ÿÙþÿÝàspyder-kernels-3.0.3/spyder_kernels/utils/tests/data.mat000066400000000000000000000007421475072611100234420ustar00rootroot00000000000000MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Fri Feb 3 10:55:39 2017 IM$xœãc``0b6 æ€Ò À å3Â1#ƒ#fÒL@lª/xœãc``pb6 æ€Ò À å31˜fdpÒL0>3 +;'D=(\/xœãc``pb6 æ€Ò À å3C1#:i& æÉ1³0²r²±3AÕ'®ódxœãcF6 ÅÄL À å3Â1#ƒ Xœ¬$Ò“ÈÄ ¤ù€Ø‚a sØ 4@ùʼn)‰Å)˜úÙpº8¡â^%7ªÿapÀÐÏ_?ØG$[„ TxœãcF6 Åa‚+ŸL32¸i> 6`@¨g阀<&f°>ôq±8@¨ûÏžEÊÌSš§ä0k&ì´Êp‘spyder-kernels-3.0.3/spyder_kernels/utils/tests/export_data.spydata000066400000000000000000000500001475072611100257170ustar00rootroot00000000000000././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1672288330.41884 export_data.pickle0000664000175000017500000000043714353214112013255 0ustar00nicknick€}q(XAqKXBqXhamqXDq}qXaqˆsXEq]qG@EaX__saved_arrays__q }q (XCq N†q Xexport_data_0000.npyq hXbq†qXexport_data_0001.npyqhK†qXexport_data_0002.npyqhK†qXexport_data_0003.npyqhK†qXexport_data_0004.npyquu.././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1672288330.41884 export_data_0000.npy0000664000175000017500000000031014353214112013241 0ustar00nicknick“NUMPYv{'descr': '