pax_global_header00006660000000000000000000000064145131522670014520gustar00rootroot0000000000000052 comment=c7be66d6c986794e77596d468dbfca1684979493 transmission-rpc-7.0.3/000077500000000000000000000000001451315226700150425ustar00rootroot00000000000000transmission-rpc-7.0.3/.github/000077500000000000000000000000001451315226700164025ustar00rootroot00000000000000transmission-rpc-7.0.3/.github/codeql/000077500000000000000000000000001451315226700176515ustar00rootroot00000000000000transmission-rpc-7.0.3/.github/codeql/codeql-config.yaml000066400000000000000000000001121451315226700232410ustar00rootroot00000000000000name: "CodeQL config" paths-ignore: - .venv paths: - transmission_rpc transmission-rpc-7.0.3/.github/renovate.json000066400000000000000000000002241451315226700211160ustar00rootroot00000000000000{ "extends": [ "github>Trim21/renovate-config", "github>Trim21/renovate-config:monthly", "github>Trim21/renovate-config:poetry" ] } transmission-rpc-7.0.3/.github/workflows/000077500000000000000000000000001451315226700204375ustar00rootroot00000000000000transmission-rpc-7.0.3/.github/workflows/build.yaml000066400000000000000000000007541451315226700224300ustar00rootroot00000000000000name: build on: push: branches: - master pull_request: branches: - master jobs: dist-files: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: "3.x" - uses: trim21/setup-poetry@dist/v1 - run: poetry build - name: Install twine run: pip install twine - name: Check Files run: twine check dist/* transmission-rpc-7.0.3/.github/workflows/ci.yaml000066400000000000000000000021231451315226700217140ustar00rootroot00000000000000name: test on: push: branches: - master pull_request: branches: - master jobs: test: runs-on: ubuntu-latest strategy: max-parallel: 2 matrix: transmission: ["version-3.00-r8", "4.0.3"] python: ["3.8", "3.9", "3.10", "3.11", "3.12"] services: transmission: image: linuxserver/transmission:${{ matrix.transmission }} ports: - 8080:9091 - 6881:6881 env: UID: "1000" TZ: Etc/UTC USER: admin PASS: my-secret-password steps: - uses: actions/checkout@v4 with: fetch-depth: 2 - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - uses: trim21/setup-poetry@dist/v1 - uses: trim21/install-poetry-project@dist/v1 - name: test run: coverage run -m pytest env: TR_PORT: 8080 TR_USER: admin TR_PASSWORD: my-secret-password - uses: codecov/codecov-action@v3 with: flags: "${{ matrix.python }}" transmission-rpc-7.0.3/.github/workflows/codeql-analysis.yaml000066400000000000000000000017571451315226700244250ustar00rootroot00000000000000name: CodeQL on: push: branches: [master] pull_request: branches: [master] schedule: - cron: 0 3 * * 5 jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [python] # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] steps: - name: Checkout repository uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.11' - uses: Trim21/setup-poetry@dist/v1 - uses: Trim21/install-poetry-project@dist/v1 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yaml setup-python-dependencies: false - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 transmission-rpc-7.0.3/.github/workflows/lint.yaml000066400000000000000000000022261451315226700222730ustar00rootroot00000000000000name: lint on: push: branches: - master pull_request: branches: - master jobs: mypy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.10' - uses: Trim21/setup-poetry@dist/v1 - uses: Trim21/install-poetry-project@dist/v1 - uses: liskin/gh-problem-matcher-wrap@v2 with: action: add linters: mypy - name: mypy run: mypy --show-column-numbers transmission_rpc ruff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.10' - uses: Trim21/setup-poetry@dist/v1 - uses: Trim21/install-poetry-project@dist/v1 - run: ruff check . --format=github pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.10' - uses: trim21/setup-poetry@dist/v1 - uses: trim21/install-poetry-project@dist/v1 - uses: trim21/actions/pre-commit@master transmission-rpc-7.0.3/.github/workflows/release.yaml000066400000000000000000000015431451315226700227460ustar00rootroot00000000000000name: release on: push: tags: - v* jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v4 - uses: Trim21/setup-poetry@dist/v1 - run: poetry publish --build env: POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} - name: Generate Changelog id: changelog uses: requarks/changelog-action@v1.9.0 with: token: ${{ github.token }} tag: ${{ github.ref_name }} writeToFile: false restrictToTypes: feat,fix,revert - name: Upload Github Release run: gh release create "${GITHUB_REF}" --notes "${CHANGELOG}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CHANGELOG: "${{ steps.changelog.outputs.changes }}" transmission-rpc-7.0.3/.gitignore000066400000000000000000000025341451315226700170360ustar00rootroot00000000000000node_modules/ .task/ # Created by .ignore support plugin (hsz.mobi) ### Python template # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python docs/source/_build/ docs/build/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt pip-wheel-metadata # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # IDE .vscode/ .idea/ app.py transmission-rpc-7.0.3/.pre-commit-config.yaml000066400000000000000000000030261451315226700213240ustar00rootroot00000000000000default_stages: [commit] repos: - repo: https://github.com/python-poetry/poetry rev: "1.4.0" hooks: - id: poetry-check - id: poetry-lock name: poetry-lock-check args: ["--check"] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-case-conflict - id: check-ast - id: check-builtin-literals - id: check-byte-order-marker - id: check-toml - id: check-yaml - id: check-json - id: check-docstring-first - id: check-merge-conflict - id: check-added-large-files # check for file bigger than 500kb - id: debug-statements - id: trailing-whitespace - id: mixed-line-ending args: [--fix=lf] - id: end-of-file-fixer - id: fix-encoding-pragma args: [--remove] - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/asottile/pyupgrade rev: v3.4.0 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/PyCQA/autoflake rev: v2.1.1 hooks: - id: autoflake args: - --in-place - --remove-unused-variables - --remove-all-unused-imports - --expand-star-imports - repo: https://github.com/PyCQA/doc8.git rev: v1.1.1 hooks: - id: doc8 name: Check rst file args: [--max-line-length=120] files: \.rst$ - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black transmission-rpc-7.0.3/.readthedocs.yaml000066400000000000000000000010771451315226700202760ustar00rootroot00000000000000version: 2 build: os: "ubuntu-22.04" tools: python: "3.10" jobs: post_create_environment: # Install poetry # https://python-poetry.org/docs/#installing-manually - python -m pip install poetry # Tell poetry to not use a virtual environment - python -m poetry config virtualenvs.create false post_install: # Install dependencies with 'docs' dependency group # https://python-poetry.org/docs/managing-dependencies/#dependency-groups - python -m poetry install --with docs sphinx: configuration: docs/conf.py transmission-rpc-7.0.3/LICENSE000066400000000000000000000021471451315226700160530ustar00rootroot00000000000000MIT License Copyright (c) 2018-2023 Trim21 Copyright (c) 2008-2014 Erik Svensson 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. transmission-rpc-7.0.3/README.md000066400000000000000000000043451451315226700163270ustar00rootroot00000000000000# Transmission-rpc Readme [![PyPI](https://img.shields.io/pypi/v/transmission-rpc)](https://pypi.org/project/transmission-rpc/) [![Documentation Status](https://readthedocs.org/projects/transmission-rpc/badge/)](https://transmission-rpc.readthedocs.io/) [![ci](https://github.com/Trim21/transmission-rpc/workflows/ci/badge.svg)](https://github.com/Trim21/transmission-rpc/actions) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/transmission-rpc)](https://pypi.org/project/transmission-rpc/) [![Codecov branch](https://img.shields.io/codecov/c/github/Trim21/transmission-rpc/master)](https://codecov.io/gh/Trim21/transmission-rpc/branch/master) `transmission-rpc` is a python wrapper on top of [transmission](https://github.com/transmission/transmission) JSON RPC protocol, hosted on GitHub at [github.com/trim21/transmission-rpc](https://github.com/trim21/transmission-rpc) ## Introduction `transmission-rpc` is a python module implementing the json-rpc client protocol for the BitTorrent client Transmission. Support 14 <= rpc version <= 17 (2.40 <= transmission version <= 4.0.4), should works fine with newer rpc version but some new feature may be missing. ## versioning `transmission-rpc` follow [Semantic Versioning](https://semver.org/), report an issue if you found unexpected API break changes at same major version. ## Install ```console pip install transmission-rpc -U ``` ## Documents ## Contributing All kinds of PRs (docs, feature, bug fixes and eta...) are most welcome. ### Setup Local Development Environment At first, you need to install [python](https://python.org/), [poetry](https://python-poetry.org/) and [task](https://taskfile.dev/) (or you can also run command in `taskfile.yaml` directly). It's recommended to python3.10 as local development python version. ```shell poetry install --sync poetry run pre-commit install # install git pre-commit hooks ``` ### Lint ```shell task lint ``` ### Testing You need to have a transmission daemon running then add a `.env` file ```shell export TR_HOST="..." export TR_PORT="..." export TR_USER="..." export TR_PASS="..." ``` ```shell task test ``` ## License `transmission-rpc` is licensed under the MIT license. transmission-rpc-7.0.3/Taskfile.yaml000066400000000000000000000005721451315226700174740ustar00rootroot00000000000000version: '3' dotenv: - .env tasks: default: cmds: - task --list-all silent: true lint: cmds: - poetry run ruff . - poetry run mypy --show-column-numbers transmission_rpc - poetry run pre-commit run --all-files test: cmds: - poetry run pytest build:docs: cmds: - poetry run sphinx-build ./docs/ ./docs/dist/ transmission-rpc-7.0.3/docs/000077500000000000000000000000001451315226700157725ustar00rootroot00000000000000transmission-rpc-7.0.3/docs/client.rst000066400000000000000000000011761451315226700200070ustar00rootroot00000000000000Client ======== Client is the class handling the Transmission JSON-RPC client protocol. Torrent ids ------------ Many functions in Client takes torrent id. You can find torrent-ids spec in `official docs `_ .. automodule:: transmission_rpc .. autofunction:: from_url .. autoclass:: Client :members: Timeouts -------- Since most methods results in HTTP requests against Transmission, it is possible to provide a argument called ``timeout``. Default timeout is 30 seconds. .. toctree:: :maxdepth: 2 :caption: Contents: transmission-rpc-7.0.3/docs/conf.py000066400000000000000000000065631451315226700173030ustar00rootroot00000000000000# # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # -- Project information ----------------------------------------------------- project = "transmission-rpc" copyright = "2018-2023, Trim21 " author = "Trim21 " # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.napoleon", "furo.sphinxext", ] # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = ".rst" # The master toctree document. master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "furo" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = { "source_edit_link": "https://github.com/trim21/transmission-rpc/blob/master/docs/source/{filename}", } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # autodoc_member_order = "bysource" autodoc_class_signature = "separated" autodoc_typehints = "both" # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = "transmission-rpc doc" transmission-rpc-7.0.3/docs/const.rst000066400000000000000000000003461451315226700176550ustar00rootroot00000000000000Const ============================================ .. automodule:: transmission_rpc.constants .. autoclass:: RatioLimitMode :members: .. autoclass:: IdleMode :members: .. toctree:: :maxdepth: 2 :caption: Contents: transmission-rpc-7.0.3/docs/errors.rst000066400000000000000000000002371451315226700200420ustar00rootroot00000000000000Errors ============================================ .. automodule:: transmission_rpc.error :members: .. toctree:: :maxdepth: 2 :caption: Contents: transmission-rpc-7.0.3/docs/examples/000077500000000000000000000000001451315226700176105ustar00rootroot00000000000000transmission-rpc-7.0.3/docs/examples/change_torrent_file.py000066400000000000000000000005421451315226700241640ustar00rootroot00000000000000from transmission_rpc import Client client = Client() t = client.get_torrent(0) client.change_torrent( t.hashString, files_unwanted=[f.id for f in t.get_files() if f.name.endswith(".txt")], priority_high=[f.id for f in t.get_files() if f.name.endswith(".mp4")], priority_low=[f.id for f in t.get_files() if f.name.endswith(".txt")], ) transmission-rpc-7.0.3/docs/examples/move_torrent_data.py000066400000000000000000000002501451315226700236730ustar00rootroot00000000000000from transmission_rpc import Client client = Client() t = client.get_torrent(0) client.move_torrent_data(t.hashString, location="/home/trim21/downloads/completed/") transmission-rpc-7.0.3/docs/examples/quick-start.py000066400000000000000000000015421451315226700224330ustar00rootroot00000000000000import requests from transmission_rpc import Client torrent_url = "https://github.com/trim21/transmission-rpc/raw/v4.1.0/tests/fixtures/iso.torrent" c = Client(host="localhost", port=9091, username="transmission", password="password") c.add_torrent(torrent_url) ######## c = Client(username="transmission", password="password") torrent_url = "magnet:?xt=urn:btih:e84213a794f3ccd890382a54a64ca68b7e925433&dn=ubuntu-18.04.1-desktop-amd64.iso" c.add_torrent(torrent_url) ######## c = Client(username="trim21", password="123456") torrent_url = "https://github.com/trim21/transmission-rpc/raw/v4.1.0/tests/fixtures/iso.torrent" r = requests.get(torrent_url) # client will base64 the torrent content for you. c.add_torrent(r.content) # or use a file-like object with open("a", "wb") as f: f.write(r.content) with open("a", "rb") as f: c.add_torrent(f) transmission-rpc-7.0.3/docs/examples/set_download_upload_speed.py000066400000000000000000000003171451315226700253710ustar00rootroot00000000000000from transmission_rpc import Client client = Client() client.change_torrent( 0, upload_limited=True, # don't forget this upload_limit=100, download_limited=True, download_limit=100, ) transmission-rpc-7.0.3/docs/index.rst000066400000000000000000000034431451315226700176370ustar00rootroot00000000000000.. transmission-rpc documentation master file, created by sphinx-quickstart on Fri Oct 5 09:29:21 2018. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to transmission-rpc's documentation! ============================================ :code:`transmission-rpc` is a python3 library to help your control your transmission daemon remotely. quick start ------------------------- .. literalinclude:: examples/quick-start.py .. seealso:: :py:meth:`transmission_rpc.client.Client.add_torrent` Example ======= Filter files .. literalinclude:: examples/change_torrent_file.py Move Torrent Data .. literalinclude:: examples/move_torrent_data.py Set Upload/Download Speed Limit .. literalinclude:: examples/set_download_upload_speed.py Arguments ------------------- Each method has it own arguments. You can pass arguments as kwargs when you call methods. But in python, :code:`-` can't be used in a variable name, so you need to replace :code:`-` with :code:`_`. For example, :code:`torrent-add` method support arguments :code:`download-dir`, you should call method like this. .. code-block :: python from transmission_rpc import Client Client().add_torrent(torrent_url, download_dir='/path/to/download/dir') :code:`transmission-rpc` will put :code:`{"download-dir": "/path/to/download/dir"}` in arguments. you can find rpc version by transmission version from `transmission rpc docs `_ .. toctree:: :maxdepth: 2 :caption: Contents: client.rst torrent.rst const.rst session.rst errors.rst utils.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` transmission-rpc-7.0.3/docs/session.rst000066400000000000000000000005601451315226700202100ustar00rootroot00000000000000Session ======= .. automodule:: transmission_rpc.session .. autoclass:: Session :inherited-members: get :members: :undoc-members: :exclude-members: __new__, __init__ .. autoclass:: SessionStats :inherited-members: get :members: :undoc-members: :exclude-members: __new__, __init__ .. toctree:: :maxdepth: 2 :caption: Contents: transmission-rpc-7.0.3/docs/torrent.rst000066400000000000000000000010361451315226700202210ustar00rootroot00000000000000Torrent ============================================ .. automodule:: transmission_rpc.torrent .. autoclass:: Torrent :members: .. autoclass:: Status :exclude-members: __new__, __init__ .. autoattribute:: Status.stopped .. autoattribute:: Status.check_pending .. autoattribute:: Status.checking .. autoattribute:: Status.download_pending .. autoattribute:: Status.downloading .. autoattribute:: Status.seed_pending .. autoattribute:: Status.seeding .. toctree:: :maxdepth: 2 :caption: Contents: transmission-rpc-7.0.3/docs/utils.rst000066400000000000000000000006011451315226700176610ustar00rootroot00000000000000.. transmission-rpc documentation master file, created by sphinx-quickstart on Fri Oct 5 09:29:21 2018. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Utils ============================================ .. automodule:: transmission_rpc.utils :members: .. toctree:: :maxdepth: 2 :caption: Contents: transmission-rpc-7.0.3/poetry.lock000066400000000000000000002765041451315226700172540ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = ">=3.6" files = [ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] [[package]] name = "babel" version = "2.12.1" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, ] [[package]] name = "beautifulsoup4" version = "4.12.2" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] html5lib = ["html5lib"] lxml = ["lxml"] [[package]] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, ] [[package]] name = "cfgv" version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] name = "charset-normalizer" version = "3.3.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.3.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, ] [package.extras] toml = ["tomli"] [[package]] name = "distlib" version = "0.3.7" description = "Distribution utilities" optional = false python-versions = "*" files = [ {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] [[package]] name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] name = "exceptiongroup" version = "1.1.3" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "filelock" version = "3.12.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, ] [package.extras] docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] typing = ["typing-extensions (>=4.7.1)"] [[package]] name = "furo" version = "2023.9.10" description = "A clean customisable Sphinx documentation theme." optional = false python-versions = ">=3.8" files = [ {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, ] [package.dependencies] beautifulsoup4 = "*" pygments = ">=2.7" sphinx = ">=6.0,<8.0" sphinx-basic-ng = "*" [[package]] name = "identify" version = "2.5.30" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ {file = "identify-2.5.30-py2.py3-none-any.whl", hash = "sha256:afe67f26ae29bab007ec21b03d4114f41316ab9dd15aa8736a167481e108da54"}, {file = "identify-2.5.30.tar.gz", hash = "sha256:f302a4256a15c849b91cfcdcec052a8ce914634b2f77ae87dad29cd749f2d88d"}, ] [package.extras] license = ["ukkonen"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] [[package]] name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] [[package]] name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] [[package]] name = "multidict" version = "6.0.4" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] [[package]] name = "mypy" version = "1.5.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies] setuptools = "*" [[package]] name = "packaging" version = "23.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "platformdirs" version = "3.10.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, ] [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" version = "3.4.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.8" files = [ {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, ] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" [[package]] name = "pygments" version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] plugins = ["importlib-metadata"] [[package]] name = "pytest" version = "7.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-github-actions-annotate-failures" version = "0.2.0" description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions" optional = false python-versions = ">=3.7" files = [ {file = "pytest-github-actions-annotate-failures-0.2.0.tar.gz", hash = "sha256:844ab626d389496e44f960b42f0a72cce29ae06d363426d17ea9ae1b4bef2288"}, {file = "pytest_github_actions_annotate_failures-0.2.0-py3-none-any.whl", hash = "sha256:8bcef65fed503faaa0524b59cfeccc8995130972dd7b008d64193cc41b9cde85"}, ] [package.dependencies] pytest = ">=4.0.0" [[package]] name = "pytz" version = "2023.3.post1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, ] [[package]] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" version = "0.0.291" description = "An extremely fast Python linter, written in Rust." optional = false python-versions = ">=3.7" files = [ {file = "ruff-0.0.291-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b97d0d7c136a85badbc7fd8397fdbb336e9409b01c07027622f28dcd7db366f2"}, {file = "ruff-0.0.291-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6ab44ea607967171e18aa5c80335237be12f3a1523375fa0cede83c5cf77feb4"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04b384f2d36f00d5fb55313d52a7d66236531195ef08157a09c4728090f2ef0"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b727c219b43f903875b7503a76c86237a00d1a39579bb3e21ce027eec9534051"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87671e33175ae949702774071b35ed4937da06f11851af75cd087e1b5a488ac4"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b75f5801547f79b7541d72a211949754c21dc0705c70eddf7f21c88a64de8b97"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b09b94efdcd162fe32b472b2dd5bf1c969fcc15b8ff52f478b048f41d4590e09"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d5b56bc3a2f83a7a1d7f4447c54d8d3db52021f726fdd55d549ca87bca5d747"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13f0d88e5f367b2dc8c7d90a8afdcfff9dd7d174e324fd3ed8e0b5cb5dc9b7f6"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b3eeee1b1a45a247758ecdc3ab26c307336d157aafc61edb98b825cadb153df3"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6c06006350c3bb689765d71f810128c9cdf4a1121fd01afc655c87bab4fb4f83"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_i686.whl", hash = "sha256:fd17220611047de247b635596e3174f3d7f2becf63bd56301fc758778df9b629"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5383ba67ad360caf6060d09012f1fb2ab8bd605ab766d10ca4427a28ab106e0b"}, {file = "ruff-0.0.291-py3-none-win32.whl", hash = "sha256:1d5f0616ae4cdc7a938b493b6a1a71c8a47d0300c0d65f6e41c281c2f7490ad3"}, {file = "ruff-0.0.291-py3-none-win_amd64.whl", hash = "sha256:8a69bfbde72db8ca1c43ee3570f59daad155196c3fbe357047cd9b77de65f15b"}, {file = "ruff-0.0.291-py3-none-win_arm64.whl", hash = "sha256:d867384a4615b7f30b223a849b52104214442b5ba79b473d7edd18da3cde22d6"}, {file = "ruff-0.0.291.tar.gz", hash = "sha256:c61109661dde9db73469d14a82b42a88c7164f731e6a3b0042e71394c1c7ceed"}, ] [[package]] name = "setuptools" version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "soupsieve" version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] [[package]] name = "sphinx" version = "7.2.6" description = "Python documentation generator" optional = false python-versions = ">=3.9" files = [ {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"}, {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" requests = ">=2.25.0" snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.9" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"] [[package]] name = "sphinx-basic-ng" version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." optional = false python-versions = ">=3.7" files = [ {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, ] [package.dependencies] sphinx = ">=4.0" [package.extras] docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] [[package]] name = "sphinxcontrib-applehelp" version = "1.0.7" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"}, {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"}, ] [package.dependencies] Sphinx = ">=5" [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "1.0.5" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"}, {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"}, ] [package.dependencies] Sphinx = ">=5" [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.0.4" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"}, {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"}, ] [package.dependencies] Sphinx = ">=5" [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "1.0.6" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"}, {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"}, ] [package.dependencies] Sphinx = ">=5" [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "1.1.9" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"}, {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"}, ] [package.dependencies] Sphinx = ">=5" [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "types-requests" version = "2.31.0.7" description = "Typing stubs for requests" optional = false python-versions = ">=3.7" files = [ {file = "types-requests-2.31.0.7.tar.gz", hash = "sha256:4d930dcabbc2452e3d70728e581ac4ac8c2d13f62509ad9114673f542af8cb4e"}, {file = "types_requests-2.31.0.7-py3-none-any.whl", hash = "sha256:39844effefca88f4f824dcdc4127b813d3b86a56b2248d3d1afa58832040d979"}, ] [package.dependencies] urllib3 = ">=2" [[package]] name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] name = "urllib3" version = "2.0.6" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" version = "20.24.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<4" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "yarl" version = "1.9.2" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" [[package]] name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [metadata] lock-version = "2.0" python-versions = "^3.8" content-hash = "d304eaca5ebde7c360513bd3d12f13bf76d30fe96ca66a2a56dc0bf3a4bd6b1a" transmission-rpc-7.0.3/pyproject.toml000066400000000000000000000060451451315226700177630ustar00rootroot00000000000000[build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry] name = "transmission-rpc" version = "7.0.3" description = "Python module that implements the Transmission bittorent client JSON-RPC protocol" authors = ["Trim21 "] readme = 'README.md' repository = 'https://github.com/Trim21/transmission-rpc' license = 'MIT' packages = [{ include = 'transmission_rpc' }] keywords = ['transmission', 'rpc'] classifiers = [ 'Intended Audience :: Developers', 'Development Status :: 4 - Beta', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3 :: Only', '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', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ] [tool.poetry.dependencies] python = "^3.8" # dependencies requests = "^2.23.0" typing-extensions = "*" [tool.poetry.group.docs.dependencies] sphinx = { version = "^7.0.0", python = "^3.9" } furo = { version = "2023.9.10", python = "^3.9" } [tool.poetry.group.dev.dependencies] yarl = "==1.9.2" # tests pytest = "==7.4.2" pytest-github-actions-annotate-failures = "==0.2.0" coverage = "==7.3.1" # linter and formatter pre-commit = { version = "==3.4.0", markers = "implementation_name != 'pypy'", python = "^3.9" } mypy = { version = "==1.5.1", markers = "implementation_name != 'pypy'", python = "^3.9" } ruff = "0.0.291" # stubs types-requests = "==2.31.0.7" pytz = "==2023.3.post1" [tool.poetry-plugin-bump] commit_msg = 'bump: v{version}' [tool.isort] default_section = 'THIRDPARTY' indent = ' ' profile = 'black' known_first_party = 'transmission_rpc' length_sort = true line_length = 120 no_lines_before = 'LOCALFOLDER' use_parentheses = true include_trailing_comma = true [tool.pytest.ini_options] addopts = '-rav -Werror' [tool.mypy] python_version = "3.8" disallow_untyped_defs = true ignore_missing_imports = true warn_return_any = false warn_unused_configs = true show_error_codes = true [tool.black] line-length = 120 target-version = ['py38'] [tool.ruff] extend-exclude = ["docs"] line-length = 120 select = [ "B", "C", "E", "F", "G", "I", "N", "Q", "S", "W", "BLE", "EXE", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "YTT", ] ignore = [ 'PLR0911', 'INP001', 'N806', 'N802', 'N803', 'E501', 'BLE001', 'RUF002', 'S301', 'S314', 'S101', 'N815', 'S104', 'C901', 'ISC003', 'PLR0913', 'I001', 'RUF001', 'SIM108', 'TCH003', 'RUF003', 'RET504', 'TRY300', 'TRY003', 'TRY201', 'TRY301', 'PLR0912', 'PLR0915', 'PLR2004', 'PGH003', ] target-version = "py38" transmission-rpc-7.0.3/tests/000077500000000000000000000000001451315226700162045ustar00rootroot00000000000000transmission-rpc-7.0.3/tests/conftest.py000066400000000000000000000013351451315226700204050ustar00rootroot00000000000000import os import secrets import pytest from transmission_rpc import LOGGER from transmission_rpc.client import Client HOST = os.getenv("TR_HOST", "127.0.0.1") PORT = int(os.getenv("TR_PORT", "9091")) USER = os.getenv("TR_USER", "admin") PASSWORD = os.getenv("TR_PASSWORD", "password") @pytest.fixture() def tr_client(): LOGGER.setLevel("INFO") with Client(host=HOST, port=PORT, username=USER, password=PASSWORD) as c: for torrent in c.get_torrents(): c.remove_torrent(torrent.id, delete_data=True) yield c for torrent in c.get_torrents(): c.remove_torrent(torrent.id, delete_data=True) @pytest.fixture() def fake_hash_factory(): return lambda: secrets.token_hex(20) transmission-rpc-7.0.3/tests/fixtures/000077500000000000000000000000001451315226700200555ustar00rootroot00000000000000transmission-rpc-7.0.3/tests/fixtures/iso.torrent000066400000000000000000002221541451315226700222740ustar00rootroot00000000000000d8:announce39:http://torrent.ubuntu.com:6969/announce13:announce-listll39:http://torrent.ubuntu.com:6969/announceel44:http://ipv6.torrent.ubuntu.com:6969/announceee7:comment29:Ubuntu CD releases.ubuntu.com13:creation datei1532624126e4:infod6:lengthi1953349632e4:name32:ubuntu-18.04.1-desktop-amd64.iso12:piece lengthi524288e6:pieces74520:º µ§-0%»Ç«´Xyvc¾‚_RL‹ÔÄX¿’ðëU¸>‰ãÙÅl½Å$´ÃÌ©Ÿ÷­`øè羌­º~)ˆ‘Öv÷…'‚¸´>fhk¶­ÙddÏx²PÆheÞ)°dsYÀ¾-×ñ¼»ã±n¼üÃ)#YjëRñÐW,“÷ôG+T äÈ{-m4”ý²vvl•ßÁ¹w¶ñÁ‰ Üœ–r€yS¥^ëÚu㤿˵Îxâ xejk¤½pÐC—×ဠÎ.y^Ì¿a¤µ<ƒj¹§ØQ ÜÊCc ÆÌŒôb”%c¼´Y_´´5Û>z'ã=K¡ú. xïƒW6IþI¯óüg-ƒayú…Sئ(Ü{øÚ'Ò´>¢jFØø|œèËãóüF]˜-·ÒŸ}AœêRw@è½n†*ÀKCƒ›ã†ü!\ÄJq…FÝ`)­Qž=´eˆñÙœ&%£±aI×’©ò]JÍçiÝ7AÕÒdä÷%â„¶M¦åDŸ˜ˆ˜m£ï´Æâ-ħ—<¦Xõ±¢yOEµþ._p×µ_ù‰ÁÍ‹+—v'wk;I G$6Ž1aÍÎN™3PŸSq¶‚×›$´õþ3Øj[œO9JŸrŠêãêGªqûÄgnš‘©¿V¦K·ÚzWQ‚pÙØZ+7xÃ?Öë·«¤™\VGÓ ³OFNüâ4a§70@§AD«&SCöžµ¬µ=ã~‚q¾sÉXÊ83h"ëæ¾£ù~UÜŠŸF¨àìJÝ÷'Ò³ù YB³ÍÃV&¼ûWlî†ü b…da?Ðt&ícÈ,UN?ÐøÙê.ÈÇÃmkmeãz´›i½ŸöƒªÄ]?m}©X:ŸÖÇ0´X[í£›FLwo߯½<àYü"ñè( ¤š´k[¹É¼¾¥†ð“!@ÕÓ{ŧ’rf>KÆjIà?÷"Îà"’Eám½<]j2¥­õï£c×dÏi¤ûÉÿˆßòî4a±/ààqMKßÅ£¯@ÚQÁ.,ÒnËÚB^P‰ÆîšÏc#au–x%ß;ºT¢‹ÏÝŽÄw, š8H]Ü(ˆ‚["‡y>ft%lw€ »“¥ôêî½ WÍM²ÿ¹mQg¨°ªf®Î|1c@©vWË3æÍÓYø¯g3üEhlÔž[< [­¨+õ+©¼ïåð_¬) •9ë‰ N}òºoñ„¯x3 ¡!,Ik3yi]¤å «Ø6'ÔOE7É­gr¡¾Ý7µ¶)U<©CXóQfú*#Åê ÕÄL ©Ø½Ýo[÷PœÃæ÷W±_sÿR‚e4¬ŸxÛ …5ŸüƒôrîMÜÖ÷rlv­÷œàq@;lLƒ!D¦¹ÝÕ@+LÜnp=æ7/³az±< ”>0\>‘A0Ê–UehЗæËª§”¹V~³àœá(ÿìGbnlS…4ô:nªŽLýùš¾³3ù´Àí!#ñÁO‹UV fÞ\ù°É±‹Êè©§Ë/ó7’¦Ê,½¥¾"ƒÐØg"»õ–‚ ÆNäËœ†è"-®¡©±L~Ýä™Q\·¨ÄÄ 2M‡½ ))Œâ6°CóÛ´ÔNgÉÝà}\ç›d7WOä­wàP©ì•7t3Úã£S ôX‹¸ à†­• ¯r §17x't æfÌÔ¡„Ãg§X–±}áB’ÙYã?¡ê]á®-öy¤¹ÝáðCµ0À_1©ÅòÇâÍ(Ì´k¯ F|.±Ž0&6 xìÞ\´(îýÕ' [îëñ•ü“¢rý•¬EãË*ÔfJ\ÉsbVÝnÄK›gÚ­i©Y$bˆ;§sWr£!tÄ„f˜1jZ›Eãµéu’Û­d~BŠýÏ0>Btœü[´9’XYúv˜¢Ej4ñ)º†}<¢.ÐÀÙÛZ9Œ›êûò—’û€Æý¨}î@iå.·(W¦CºÝZ4rº]ô¿È¡•XëŸÀÊAÈ6åD„“…A¿Ì‰™CZ¥t­C¢Rmj Ió‰0¯•wÓùÛ¨Jýk_ ?º®ßHD!…æ¹ÿÓ-ÓÈö£@ó'kSõÛQ±È\°À¨òö)9ãI‚¨Ðö– dYÿi2…#X½ÜÉaºxŽ#Ò“lÔ -òxZ0Sެ’¼aº[h;‡,5Ü^¿÷uº Çgfܸ¤¯sY±½»m:Õê«S)'ÅÎ(›NÔ Ól¸]±/Ø‚†Qe$„¨FÕˆqÍqJJõ“ƒQØÁ)«Uï_Æ:޹§¼¬áȇÈl³™²°‰Áñõä Õ?\•²³3ôЉDìˆ9¨b½à#k‘ gÉIŽ‹H'M8Xáò ¤6m´hmºÄ¬(WÜ“ýI{Cdh›ÉåÃXqíb›X„[• r&Yî§×´­\"Ó±&ß Fñ³V«‰5aõz*þ4j\¹%ênšP(H·ÄžÃf4¯p±pÒ~}š„†‚þç:V ¥½C‚îð¡ª5iÇ‹«’ž%!.ÕJ­ôå®”U)GÇ-¶ÇŠ&0$¯,*Æ>‚³HÒ!Æ«^h°Ëó:öËÓ÷ÀPa ˜k¼¶‚Û;÷"ýb®à ²ÔãÙ2©¶ÿ^>Î!zd×,‡,ë5öìθŸ(åv•ˆ¼+zrZÓ(ã›w&¯(wr+R‰£êrÄΚãàʬD{lŸQÞS;¶1f#² ä¨â^Qðl-¡Š> dÆU°ûA;¬ò×Äì™xïõð|f?mF#RŸÒ›ýÆÞ5Å»›)£ûî€I}¾²q1ñµ§® ÓweÌ&›†~LÅyW8<D…Kš¶,è·£Šù0iAÙ€˜¦ƒùÅ›¥% ý HÀɾ¹Æ $õXçúP BEfàìVËi›ÄПö3 ÔÜÑÉ‘C+¿&wr À"h9Þ‰ºÜ§iå}ibƒo4>ø6îJµïê ÐO4‰†T¡îÓËï ò ü6QekðœÑ[92z™¢U9¯mÁ]S>&öþKãtŸƒ©ÆmËT¶_ ìƒò—Hº§yO‡†Nœ¤7èbï-M(IÚ“¾c ±<Ì–» öÀXr} -î¯ÑJ Çm*¨u•&Π𻤨-!£Ÿt¨lŠÓ"³O¤Íu '®5ò̼×ãÔç,š…Ÿ.Vu?yW{Ë fŒ‚qž»4"àx¦ûÃ׿ÝÙœiu÷§¶¯—÷ÍÁê3®à”à‹;õ£hNåξ²—Ì䘻œq|P‡mû<ñÖB†îvT<±ï¨Å6 ³3B5 Ñ>Jhì 8‡Sæh&›ƒÈX¿ëô=93PýF{)µ’ë 6JlÒqÒRcj”Ùt²Ïw ó—tåR¾>ÈðëÕ1› .E,9–+/Ð9³ªWK«W7%××3\CÅ`¿B5T¼Óë`§·'Ö¡âq»–{­’ÐŒÍË’M§O1hÈŽ™#6—¨§ÍG¡¯@å|”­7ŒèPáO`×ۘ˰Þû:LÂû…0óØû³¡^ÏP–Ž nÏkƒ¨p²k_çL.xßÑ5å{9ãìò¶^¨r´qÜ¥Fað†#½_Jß²€Q¦ ½Â7–Ç6ªOnì÷PYçêõ Ã{Ýx# ”~DkN%ÖQ¨VB¬®Š?ãîçy °"cj†£¼«ß»‰Ç*M“t¡)ìµý9…Ð_~Ñ oŒ>~7ô«œèFhÔ+žáa×h˜bÍN]DSšK1×D¼w»Óº0;oJg[˜4p@ÃqKB¿ÃåàX/ƒ”pgk”øL²q˜.Q‡>æÎ ²?µ'fÿ.‰®Át“Âü›‰qz#y&}À6§MsÞB÷Bµ?tš$Ôoƒ}%×ðŸ­ÒCÂË?©F[A ÖÉ“À.þÁ÷£3²òŠ_P*áó /Ï2›`bí@–h4ówä†@¬¡aWq©ohÁÁeŒ¶ñ§©Òžr„–ç;`fäÂAG²Å’*{ú±ZÉü+Ûò2͹Xkn™ ™.äFŸ} y]ˆª €yÆÍìÖ:•„™ÉÏ·ótƒi (Q°M‚¼û6%|<= ÝòoÈäá$°Žy/ônÂJ¾7Ð ØÄQyÕH^ÏbVBsÏ(cH¼ûV°½°¦lÇÁwP>|}xοóK;ö§kíøú,«Ò&cI2ïýŸâ?íÕ°,ŽÁNl¾‡þo‡ÀâˆC­Ðê%; aL<c®ϧ˜Œ‚`ˆËÅ×`ýZƒoOå>·ÿQ­Tƒ" ¨šàÂíùYAx+•›rôf0÷–ÃÚdýPè5(8w§›8Ô%„ü"O]gZ*1X÷…`(Õ^•àxAfúa½G-Ï”R-mÎ(BØõë'¥5½ÌÄ=y<¨ÄÉ¿#î$ÚO~‘??€Häׯ±µ‹]Ũ¶âîÆ@N7 £›îŽËèýð¶Nðb=(}i®´FäæZ:à¼âÍ "€dù´èáÑÐGõ*ägV4ëcº÷ýî³À¢ÿK'Êfè"¯©Ô:'…M<^–mÐI %Èš²ß².Óýÿ@)É£¬Á$¼Ýæ¤]2Fˆöc¨×_OÉÎî'ŠH™…"nÂî‘$ê€ÜÇlqÉyQ†l‘€E~P Tý>ÿ“ä¤ š»«%cz±µó ÖõrK¥½¹×-“$Òë3n|¼Â[uç#mwž½ëäB0îöëQ‚·:^Ñk:ÐYÉ®©°f̺È\Å.F¥¬ ŽTÔfRºÈ‘´gÀvÚIõŸïY%çþ˜šçg«¬ˆæÂ>Ö<§þÐç5‡.+žx”'Ò[¿Ÿƒ¸ªþ+”IcŸœgL¶@6ˆ(K”0âÀ Õ$? -q›“Jmý¿%E| i0El¬ä;6z9Û¿\)£& `#=êà£J|×ï¥4%ÌBís{Gþ:i~nÁÕÕþoT'п5‹—C/)Tèg^¥¶ElUÈMT½¤Ó°5Âsu“NˆƒÄ¶s Û?o÷¸Ëªü ÂjSÖOƒNwö–I礹p u1]㤮ú+º´·eÙ”ª kx±à%E1¯ã™"~"öˆ§KzŽÁõàÌî¢ô‚S¢+ZæÎÛŽXˆ1Ð(Jã·O~Í„›ƒ_õõpë{üU¦Ô|ÍÇ™aîÎðib¡àZ%3t©áV„¸’ö‚”Í‘M»†9nÎyŽ›_Œ}òI“ç¡"îñ¾w lñ¹‘Œns 0Û}éÈèì|¬U7ódÍ Q—Ä× )Éÿyµ:J¢€‚€0ùúrñ “àI&œ¯A”mЇ’ ÇUMöì!VüÝõ–äQ‹æ]z=ëÇMÈzwnª'Jä†ñÍø>5e¸%qŽ´Ø}kyüVÖׂՙȠ0zƒÐš›Kžø:pV Ìn€ÌZ‰ØY:s®÷“…xõÈd RÖëÞ§—C¹CM#Å:ÃR-h•²6ÿ—Ù]6ê%ì?óÔL<¼Fõ¢m¾‚O‘Ú„ eÔ®xœbPv=Cú¿‡E±Ö+Ši™Bãh“@úJ›óö¿.&øçȨ ñ8ÐϦ¥Ñeð4œÛs,øPÊ—ŒâRìztcxîþ—ËS )xÄJë8î·Ôº­u×ú}8U8æf–('^«CãFÐÄ‹2>×ÊÙ{…¤=én¯×'£{³‚PÔÙóZ}%û<é/bÍÙVÂß²úѤ0U ãʦ=b‘Èeÿ=6êµÉ_—êÁœ5z=^S`£ Ú.ñÑ»W†° AUsÄ¥“í¤6ÈQ¦ÆÓo¨òÁÛSpè¥Z45@)ÿ ¤•½ŒÌ“nj…ƒ?ç4`ýEJç7Á£ÇƒJOê]vø5g5ÓVûm„í-/BÙpdës¦`ìsòµÓ‚7r£óL%xA¥ô•÷4`1œÑp7YLçŽJz ðNFb uÚ1 °èbr3 ë ‰dl@@ešKÒóž8iDØ^x€X9:Þíônêì¨^¹]Þ•&Ø„=Æñ‹™§ˆ»ûØ8C€ê%³±Ïñ@åµÝÚuêᔓr ÓCl×éY»ZÏjò4XÈç†)¨Ù¾°qñÔ‘&ì(¶Ú4•oHxi×(gÈäÙ¨)DŸJ†Œ¤‹7`R²Ûž®…º×X{1®-«Ši×B±î¡O±4Òâíí¡“àhÿŠ…`9/£ßžœðíB©Jóž ^Ã>.%Ñ¡7ùÐXÝâZ6þ–Æ3*÷TM@ªŸ†¯£GnŸ~©hs½"Ùþì‰\yYüÇžRƒè 0²ð(r¤(éŠÕý!Öƒ¹ÑttQÙÏ…ÁJÔˆ1*§…¦>¼’™¦G‚ [I?uæà^tÇmå[÷pWVÕ’‰ ëT*A‘DF<ÔÄ»ÉíU¸†Íæ.HÄ´°ƒç,xÀ5^ý ‹XNÕa÷Ž ŸX³=B^•jü d“i/x¦Þå÷&pL¡WY{æ×ò ´|~¸¾Õ¼ÄÍàöh5°t5a`äSÝk¬Î€ÌT•Ú¥ä?<>yP»R^Fðò\ÐB }WÝ@k¾.9àJ9`–ØÄPݨÃx‚Âì&†¤ù ÅÂdWÜ“,Ðåc‚¡c4š£-F@àk¿¡ªJ"¦)´° aÙ±˜Ø~ñÞ~…}Ô¬Vc=mœÿ&Oì<§DH~Gé›Å´+iqשB°PG’áø~LTª»2­$ÿ"øÂ¶¡˜Š¡‹²”¨(8ržßV¥³ÁÔÚ)íŸýÙ5Ƈê£ó¬yÍž5\KóH™ê6îsLÚù B<–áºØƒÂ+)CÒ¸†\¬˜ Ë™˜aÙµú”›4zX$t ï•~{àÖ¨Q¾ÙEÕ‡jf˜›è(inŒ"Õ7}ˆAG° Že9ŒŠ¹OÝcè¾>kŘq‚= =l¿Ô?Å!ÀOÇ8®âÀ‹¹'T{†%6óGæbÕ3%AP°Š†(ucp²˜*MrÅDLV[Åˉ۹Â~æ¡{ªH•i%£pX|MÆOÛÙÙÌ|Áܼ¶ ˜Ñ<ºhq¬ðÏ(ÕïÀ´®Tx&Î!1¼hŠÙ^èCöÛ/BaA ÷`"X Ó¿5á‹ÛŒ7#O>3^ZúÕÓ¯‚7—оªÎ ?YéùE–ÕÖfn§p«\¿ÐT˜ëcküʸ9q6ܲÿÕ±µ{©7b'¨Ä™Û°("jû¼Ú 6É·bQ(gM|òéÆX¬‚ì•™ÀxÄþ–¥*Ûè”ðW®ìÃYsÊs¢\w("¥ þ¹‘ÜNþnÕÞ>{ß\¬í…«I:æŸ\I›â0Ï…Ja_Õ„ŽÃo’ß"x/é…Þ ö#–Ѐ— çeʇyØ[P%Z¸oþþõ[XkR3|‰ÇØ{“$<ë;ÕËÒÉ}H·‰Ð5|s£ihs¦£õIt•|Ú6Ûí¥ózö°m!‡­É`Œ¨¤9Q›H­*,uUÂ*´yö¤oOúJé+}îF†{ŸJ)c˜ÃuxVvTèÈÉfœÐ3çÜîdiú;qk¹`ŸÎYj\KaO8]pƒÛ-ºWhÊK}¶ý$IkuAçÈÓ¾r¾rΞá0Ä™1ƒX﬘€™u&V|³å½̵!ÂU(‰ÔÖcÔæ´x[|©YÖPÿÕþ|è»s‰Z0rffZ^UžÂED«l:Þìv&Ýîuv% ÒÄŒì ²úÖDNb3ÝZ&Æ^… AµÉãx<Ñ+ò—´ çÄÀüGqWÒ|ºb ¢ˆ»5)P`Þ,…åÀmOtL)X»”Ÿ6²ˆÉÕ)0 š7¿‹íh ¨¦ƒ‹ß˜õSLù²,¼¯nÑ|„¸V«ì¢Îº êÔß‘a.áŒ}7¨¯lÔÔ2’f³A<´iB:¤ ØJ±…v*èâÖ„b16>Y›j:M×DµÏè;ƒ}âé9Äž;Û+ÏGª$èž»V 4ñ1ÌÒZr¢´Õt 1ìk:©B:ßO¬~ò]ã쿤÷_ 4]بõ?Ý‹à ƒÛQNþ†×!B>Â_^ ä»xŸ%CU€ˆÙ¶ë&`gÖ‡¶n‡ÛŸÕÍCR¡9p‚°34å›Íêc´Ó¡Ä½ÇÇ—M\.Á“4öõFt8O#¢Lò¤pø4DáŸ~̉ÃÑÌ+eKG …g·‡çoe9à™z7Š8$Öâ¬ð¼žùê&¼´ÍΆz;â¥6Ö&þ1K}7ÇQ©ßèåa ~™ï>)„Ù(×'TîKî}ÛjË@s%þÃCœ›ð‹¨ý8$ÊZøe_4›”:r°Û»JÞÂígšµ2CMmŽ®Ê á…}&WZŒM8cëÎgAà!*kÜ9åvZºˆ¼­WÏsÍjöMN± i6}8¶Ä3³¾ð¶"èEôn‘7Æ»×9¼À)ꔼÞÃÉÂ0»Þ×SÞÜø±Ößòg`ÒÝ7ô°š.ÈzM¾„¨Aw}_zR·hÝg–ñ°‹${šL~Τë2G¼ìtÒT§Qˆˆ©s"ôëžûçD'•¿EÓÉb¶á†Ï|˜1Kv?Têê ‘Èf-ðec)‘²‚%Hµ¡*’òçLnxiÔÙÛ›ü%mg<ßT§Bì7ñ¢ŠUø¯¹fà§ü§Ká‚(¥vWœj½ ÷ߦøÕ€Ð¨fð‰iI’‚×Â`j£ªý?/ô|%l™~•¯²éÛ7€ ’Ÿtˆ9,êK‚b ÿÔ;í5©R{­Â7I =–F™‘Z Ý榭€¼ŸCᘯ¡G˜ÅÚfám\bO+±Ñb6²\šžz%X€3 Ü ó«è¤Ãž'¬ïígmWlG/”™€™¡0fÔuŸÓ£üG”©|^x?³ü˜¢ùáq¡% GÝ6Ø—È™XzHbKØ/ ß.AVs¦ÅéÖ»vN6Ö°n»ÿö‚°Ž[PÅœ.6­}Ÿ˜‰O_ÁŒ-™ í¤LÝøÆ,ö{”‚`bÃ:0Šô‰äß¡-Ïé[ì'óÉ@à‰Œ:}Ý ¥¯¿±jíµpÄzP¸ÔôM͹CJOúbkæegO2]øKCÂÂës¡æ {Ï#½keíµ¢ò^tøgqvj+¾ðŸ€/ÓÂ|Ï IÎ_ŠÈùzjØRH “b0ݧý÷¦ðÆÝéNÿÉÐè-¦ÌBÊ{)Fmxg¢$Z*…¬  y&e±hq|ƒ8MÐÏ\¨ò+XÀƒm3Žì´Å Jú÷˱<^§ãÇ"ãg ŒL…¹cî›õͽ~¢¹«Ó³H[pî DÕYó€ø IÏ`†8*ÃÇaës8ëñlZër¦Íô'‹Ž6} ×5Õ´O™o"ykAáÑŽa*ÍI WãYª¦€IíC(’g¨Qbº~§†{˜l¹Hìá U¯€i!®Îaa3x±ÙÿiÙv©/è®XÕ':®=Ðà„9‰ :j§Ö–ÉF6­µŠ¯ç³.”š²³(Š2 •9âý)(ŸoCŒ]t¨x@šÒ1+„q¿ô4˜²B{;F@>¸édÔ®ðkFÓ1tåèåìw/n¼~2ê¿§ûá"S.Âo3Ó-n-¬Z#ÏKõV ªÁŠ)D˺#Û·ÌæwæUïÊØ&·-+&}1rZŽ%IuRƾæ¤jÀÜÆÿ …R«ßAªGߎ纺£´iõb9ú¦þÿð,»A"tt2Sèǯõî—"ì âzíbEMR€—øÖCC†D½ðÞ¨ô儤ÍGŽÜb`lÍ«ùO†TV+ [[îÅ«ì¿P,°BŸØeLÓUOǵÝ„ ÉxͳV ¦Ú¡ÜÛ\P¤OûW õ¬Ä”柉1­ MnǬ¹Ï<ûrpÑ]ᆻvN´«Œ‰g›Ê·ÛèUÊ"%3¯HÐ⿵…At‹ÚaoZø]ÐãƒìÊsÚ‡$ ꯯ØqCd("'s¾+?+ž¦6ÔŒd?ÀÇ4R\Äd5ÊCŒ€¨X“tî ³L–9¸)/4ßLù"RQ<š|öu¿D°øHøŦp°Ær0O­šÙþë½´CO|2‰Òð}RUê¯õ™‹KœëØú0ç]®Üc8AŸ ÿ倮Äd2Fž>o™[áûä¨×ö¸ âþ@…‚ cVþ—>jV¸„ï­Ï쇶û.Fky/_Ñ÷Õ³‹uëÝá·Z„gÓ$sÀšP'×ÏÕugž`ÌóĹ³Jw©I–ÏsZû…JíRÆYü „\ ñ'\QÒðàÏö?>®?™çN“B`þ}ˆywU°¸¶‘A½ÌT*µF©ñL³N;‡S#Ä/a2\æG¦î0BrÈ-»9 ×R B©­sù…ôÇpv–Ü#spÛº¢ôlýÍ ¥8³°÷ hã8ªFýÚµܔ2FêÛYØ”¾ PèíqªŒR Ъî‚'«´üÚh0¶·¹"ÖªüsQ7éSP{¯»OJL ‡ Åýè¸!®‡àÂDªä× † @ïl}½C€ ê5~êõ”íÀ¬l&—:iî½zšèÕ•mYØ^=Uì†úQA~™‡RÜI58¦£ä3\•i¸ggm©ýÿê&BßV<¿LþcÂ"da¿þl X¦qOÛ¸|ëH¹1ón´­Ï›ŠîüÛ¼2Ú_%®ðp¾¼Â)ÈæMïÏøCN¾Š¨ƒX5âHtqm*]%ÇTÆ4÷1GÁmåË©#I¥¯Õ"[\æÄãÎÉ{¸ŒßÄZXdB-Gä£;¬Â_Þ\›¥p–}5®- AM©ag¬'^ËE®ZNÑI„ðz‘å»6µÚÎ÷—Øg²=Ïoð"c{sô<°Þ^œ¯¤¯,xñTºæ¬\‹¦‡8V~ Š€‹usɪm‡v1^D Ù ™W)矽ÖR¨Ù2I®3)Iád‹¬¥œ¤ÒÖ(ÄŸ€´Š+á@¦BEy0ALp®“Lcš³aœ³Biñýp,bÿ &þOY ÑÃté퇧xÓxâJÊBGf›?ѧ´^l2ôî°¿ÛáØÙ2¨­ª¡EMœ±LçT+oJc#5OÄ®X‹¸@Žú"$â- $’‘@dVƒSc;vPÔ½‘Üwq‰9Fä[©œ0író‰©^鞨`äSÎõ m ÿ²ö¬Z:?›™õkÑ}Ïù±Îé“¿‡`Ç}‚istT³üšO# ÌùC¨åö;,JpºK-PÞtïÿ_‰2SÉBT´ž\Ʋ¦/Sý©ð(˜ó¿[p ·{ ÓñÃÀöÙK¨ÖÇ-q÷r¬RäJž2lëy!™Cvs9–€’,{èe0€QËïJÀZtqm~Ïž,R&Äî_|Jèce~ ÒÃdxoî§ŠH“ÅsXVõ •#áY ^ì1½Ó—™&Vyñ¢…¦§ƒcÇ©ùÛ#‘“ÐÞÕŸÚiqŸâÂKOå÷É]Lƒ8ykè¶òÌ‚¹*9Òñ‰­ëä6ŸC¨›:²ƒ¥€™$€1ß5Kh©É!CË™êÞCâeùÙ*®`CËæKŠÀƒ/ý0ÿ»2lj¢@e1aýw•¨qmÉl–zq#¹êø“lB‚„Òç=ÄëD7CBCÚèåÆþÄeÛ5³ŽÓò‹ Ép;|FÅ.…„'ƒ¢³g»ge™JÊö,7ÒaøÜ·E–…_.9™n †ü7À£X·òï»Ãn’pÆã)we#rƒ=’Ž€&ª.ðˆÚ[ 7w÷ãš­Å¥fýäÏ·¸º§Â»¾h\½Ä¹ ¨÷6S6·Ò¤†0 ¡º|i%Ñ‚@ŸÙ~…kµür0·‹dOÜÉØpöMhåæ§Q¡b4¨°6eÙ¢f¢5÷²YHŸ:¡)¥n—‰º„âÜ.ÔËýŽoh…ÞÑ”1é›—š@‹“JEIbÉîöž*ûS‚jšg¹˜X[…£”·Ã¨3'Ú£*EQáhá[ŽâÆ"÷Ebºv«‡À˜× ¨¾^–ÔPÐ0ÕÕ–=•¾ŒÛdV¹Ѿ¿4RáR1Ë…¯¥ µç´4ë••±¡ÏIééè—ÆìeŽYQqY¾c$IUAINp»l*iñ0^˜M‘úC¿ê­8¡P‹Ï³½@+áü¬ܶãÃvWð ûú1ï:ypÝ+÷´VZ,úµÃ-´p/a$!Æê‘b-Mžt†ÄñžOÙÜ u éIóï%k¸•~ ,B—Á/¯„^)û¹{ž'Lœ"ô¥6K¼ã[ µØ3`…> B%ãúþÕkoUñ¤úĽݺqFí JR¾Ï_9ñTV2Û˜ÃA MŸŒlÉiE .žÃW¤§*ùêt?;§ÌÊdsìá¤üÈ„õ-§Êú‹É‡úήU «´Q#ݱ‘:Fµã¯Ï¢Æ…šú³]œk<êå­Ç+.áÍ]å=ðK¨ãÊ÷çc¶ØxÛ,º!O” ~Þ~òöàU=‘dqŠ'Ö’æ~1 tž¥®h!ÍJ±³q–ð{ú ]*Î"ÿÔrké÷K ¸Ö‰üj øØ‚¢í—¾omœÖÖêöN¡® ×ÑOØÏ,š¿þßuBã¨d:RuV˜aAM|¥9}F¢[]þefã’hñ—aÌ£Z"*¶_ÿÞu­Rð‰ ðè}B, {Ö½ýiâKÞ·<¤(ÆZ– Üé‚)!ô'`Qs_ìè#ËHo!TxÎ`v)DÉýEåsy…‡`ø¨[ÙÎe‡ÑúÏAùæSìü#××3=ËËCºËŠ»µñPS¾[Ã`½‡'oïÝN‰ýÃįqe`UŽ5[¢) ©Âq¼Sšp\’é+ ˜w÷)MS챩\ìRÁç€æøánz=Q²§íàž§C‰¨ÔNäÁòݾJžÓ§EE~$ê\˜¸è/À–Ù6luFB5gƒDê^1d%‘áèÓ97ÆXtÌES¹)s·rú öWDR 7 c{‹ï°²u½Ê1vq$¡·©¤x)¬:[:ÞeÁJL }ÔØç?Ê+†…Ѽ‚˜­fGª#W‘Óp"wVÉü’MP~yò`v6äÎ ÈÓ1»R›V+¡ÙÄï¸FñÕòcûé¢S¸‡œÿ‹p‡6âù>†ã¸rž'\‰Ê]z|1èl ì½Åù×a‚èöô„)ÛÞ¶t%þ¢yÇzfø®‡‘¦W ⓟ…ŒgÅ3%³ tÎÜ_ÏŒýIé3’¸ç@þ¸ Zæ‚Q·‘PÄÓo—*TÿeÁ„S*ï…ŽvW8yþ5#9ʬ’;ä¨ÕÝgùJâPŸ÷8&íýü¸áH›s½2! dS)" ï†ð'Öë<äÆ*ß¶e4_dÅÉ¢0Ü…a˜zçEÍÖZÃ_u=­<]忈—ûalz)Ú½ž<5Tdr2 ðý˧Þê¯Ãò@™•?¼äŸ áQܺæ¡( 1öƹi9Ū[gõd>D;o׌VjŽÄ)•–ŒyXðåàµ/:žR½OͲpnBÚRqï5pB1õŒav¶tá ¬ZÃíÂ[ƃ„qéepÓ㦠”r‰ð²%qxïe–vfâ¬q$PÐî·s7“$Î}=‹ËˆK¶6¼¬ÌSÿž5OÂàQa (ÒòqìÄk0Ðé¯z»ÔÅ íP­çù…ƒãbNPÿ;߈™<»Õ¾¼eÆê!2µ kÅã ÂZù2ðtˆBµ‡Öc¤W /â[3Õ‘ìÛ·XšÄÔÆR‡ùLèν½ç=•“ˆpIPBF•Iq[—ûlªŸF# 12­y#¼x…fÙžz\{ec?¸#ëLà*‹Lÿ˜azÛxV[€%vk^’¬˜P¿ÇxõÇ C®Y,_£4ÙŽÏÑâû™Q|²°9–-­V8¥–·V±’§³šadšµw1°8$8 n ²Óh¯Ãé›ú w…¢É `ÄÓ‹ùë£0…²¿fÞ}¿™¦Ðj³µQmRi>ÜÔžixÌoD‘§·s oܯ„˜dÅ™E[ïˆ9ǸHoܾÚLéÜ%úÆŠcÍËÎÜa†ác‚­uœŸ2þÁ$¼˜¸äûÜÕÑÐ|p’ïÆ™”â®ðô¢#®M @)N5CÎy‰m¡%%áH·ÀŽ´ï!qrŽ+ÿ‘ùÞC‚l~á ‚ söо8uÚG×…o†±?˜Ò§ ±ní!åbð(Õ°½ J§µ-wµnó€…§ö„š³ý7$Öûb3l$ÿä^ÄAÃ(Õ߉ywF”¯<©BùfâËyª0gÞx;Û#á¨éëÛ»û¢(œÒg¤„)W_GäÂш*ùnæJ]³QŽ0‚ÖžN³>+ëUfäHKž±œêMœˆÄL°¾Ÿ+­XÝd¸‰.qÛ:VT ¢£@A†°Õªµ’Xø>Ù[×­%Uнô¸c”ðS-½n^„Ð/2HP×ø4ZÆp0R¢pƒµP‡”—  ÇʤN°™ÒFmÀw‘ìÛÿâ˜V§¤ÜH&þXã/ýÜhèéÙivÍðg­Øžµð9…×~±™®ñïSwàÈúÏG«M‚”—çìמD-ÔÃ$x,I·µðúÜÒˆD¢Õ áâõeo‚ÅÃf°Ê¶N4KçÉêÍ{k$=ô?dîRQp ÷¸ëÛÕ¦–ÿ6f>–bÆ©$)$mž¬Ú¤ÃW%ÇÓ,ÂFƒË‹u±ÜÐ%»ˆΕüSïëi§dñ8ªàc™#n^ìë/ŒÞzIbª˜Ù/½Ú·ÒèM €ù¤Ø¼²Â™"ÊãÍÖ?„HxÌ¥7ÇÓVÿm'XgÒ)fº¦:e9º °g¤Û3wh. BHʃ"/*ÉÌÝKH0ð¸hZJ3™T‘ÑiC…s¸"SJf(Ρo‘Z&sÉÀ߆´ ¯¬ÉSS‰6üu Ú‚úÉŠ†/ço!ùnE¹d‘1ûŠ45·ÚŸ_à %,ר­á“Aj%µßù­«ˆúq HN›ÞØp-ÕÇ!ù8\(É)>¢ìA°S7¨|ÿ=,‰5$Âí¶A‰}¯Ü1®9¯}EwÊëåÑmÜaçAS“¼{Ñ‚HMü;¦w)>жÍá+YÞWë®'e9B9Y­Eø¬’/M3˽]|, }Ä •\ }ë@ ¼à°¦¥AÑ:p!=wXWî°bZeÝ¥‹* Zˆ“chbæs*%œ}gÒ“(ƒ–H±/^j*¨Ü¢.yS)´‰ÈÑjŠÏò¦Ã#aÔ®PÜg 0æ PQD(‹¦y<}jGzÏdˆÂ³ªKqÙ&#ŸJ¼ñ¸íí_(5K— `öJ©ÞùLEtÛIØy’˜mâC|êj4iˆfJ1}“×qÍk“\õ|¬ä²šÓkÊLBßÖÿ*·îÉí —óž.À2uI+ò®Ö·û³^) Ö™è§YKô Óífô%„bé¢óY#?‹§—¢½5JATš&¡z‡Å=’ ¬Áý6¼…”GÊì0•‘™‹åxoK¥Q¡†ŒœŽAµ¡ª‹Ð6 ¦Ž1’š´dD}P˜H¬µ¦öùÛ|àmó°»± MAd̪ÔøÅBÐÒððÑIÿn¾E­ò]?ÞfxÈ(ÁbZ&¦ÊÚƒPz¤óÐ(äŸðûC>RRœÕöU+f—áÖÒŒO¤H ¿œþ×¥= Çìh¯¸¾NB±Ü»yõ1S /ðÎKãÖ•]®é|VÌú=oç˜W¥SkܪÆ }¹ßi©&Ô¸¡}²•£©ƒßú©|VW] ÇŠ¿˜‡$¤·Ê.m¯¡q¼uÃ\/ºÞ m¸'ˆ•Ñ v?%ˆ}á5Æ”ÙïE3Ë#¼JhA`,­ñ„­Ø¡¹lÎKñûI%†ÏšÁp#¯‘SûÀ÷×$!íÏ$û{›(|ë{DŒ’í„íį,†µÄ²d[cÕ<”hä”qØk{>ÓnÑ©? å!´¢g[„ngÇ=|‰TÌ(¸¬ È~•2¶~¡EfXçQ‡º% ‰Ì¹«dàHì -Ð'hï3Ñs-;ƒ¾p¹êm”Œç©CeÏãªeh,gè­"s0óÇmòX®^ €ø›e¥$èBψÎyKá Ÿï³ó›dp ²•?f$¦XurnñÕ‡°nãžà^ËŸÝ«ûå´afl6us!ãÊQóÿ¬{¯TÙy^#lO¯ÅÂb!ÅOdL¸´DЍÉQÆÜ—m.:mIÑJÙ’4cl¢Ñœ‡OÉÊò¤ÊíÊ||¿€MËÇ|¡O¶þ›prLGi2>sqîZ gzù¼•õÐïZ7ÍH4ƒ˜rˆK~í Ys³FÖ‚ #¶Œwtÿ04¥×ÿ‰è6Ùƒ(O`¥dœgxÈç‘#þ …º%U?ΊZñoz: ŸU’„׳dþv˜ÔdbéDü~Jwý1O ªÐ<Ô#–$õÞÂßš¡Ö× q£“JÍ;ADȰ)²©`¨mg-ÚÌð¹‚b[|sŠK\ã0leš™“ßÆÍ›¡–›–‹«þn•éž3Žx˜ÊA°A/fä0Û!ÓyS*ÖEDY™øÉ(=ΟZòLÊè2ð‘Ž=¶§žTló%rÁPD”›G%‹Qmí3’+“aƒ€ð d©&b£ß¾Yùߦ‹G² Y…»ŽíVœ¬Í9n7¡†ºÞå†ß ¬-ö¦(F+«”Óäõõ”Zo6šñÁr%ë:îMûÇAÞ”Bìè³qDÒ0ºúNF3%³É”Øê¸kæ7T$‡»!ä¬dÏ©³ 2ßµ7Ô0Z—X¢¤ÙPÇj4ŸwSÓÉ*m®(æÞ-lÂÁûX×Jr!µvÆe ¢ùÂ!„Äùe¸ ûbËB‡†%( ·ÓY{ÝR>¼;/lmÜ?WF©Ö£(•¿‚ÊwïÝŠÏâºÔW‡lº:Ú96ßã$må5¢ã8I}Þ*'Â!u†û??/}‘\ïX4Ù€S@È¡RŇ3 ~eOî}-j,ëò9’`dÌçeÖ:§ GévÖY“"æ·)@0sU ƒè/-ç!^‰K^¸%é­UÃy¨!<*ØÃO‡ 5ê1ÂR+À­aÓþjÍXJü‚5й ÎÆ ÜúÓ”/Æ÷MÙÃ( —C5# §„´Ã¯ ò`ýËr?=Ÿdض»‹šã–‚£™íFÌ)ÇÇ—‰Ý :& ïa­ZÎNÕiˆÕÛÎçìÁøÚö,zHßR‹±ŽÇ$µt1ô¡–T8ÔS 2<{Ïe¬+Éñù§…/|‰ƒ`_,®Xrìù«ŒÙ|çLH·ò|œ –p¸XƒÆÄZN\­]"°,LØú…~¢ÓK%f…Ó õx$`ŒÞ[aUÊJg³<…ëi™)ÎùÏŸ©O~w%rÅ¡ê~î°÷Û2½#w€ 3µ?Ä3–:¢„rSêWwýB¸ÝJ]V?Õ…U[?Fh‡¿ýŒrLÁI§t/ß™%,NÐ)×…Üw—€ÈWÔÔ‡ôßÍ|–ºÛ¶70&•À+ ^ˆmÑcã¥âKLà›CFÛ‚Û¼LšÃoª &EYX>¸Êk^»HØ'FuSgGÃà Þº­ýìN§0/ÄPDÕCàÌçª_QÌÆ—r‘7ðºÝ6Ò/è7yÒAOž¦ dE»Ž¢~šmµVóAòIª1ŽGL.ÿK¦\'!= KË öOÙ·A}´#¹¿ù1à&÷« 1!'Àš»Ðë‘_—”}ÛgªUàú©ž&ñ`9÷RP3€‹ˆ²ƒÁ“Ò Ä7FEÁ„Ýb;¿º›1?I Z—mà«) ¼¦I>Ëß°Úa©]³þ‹@.f<€¶§£½à<[ã?­ /¬D2€Ýxþ¢“ÇÅuY æØ´9ÂùEmÞ¢¥ó‚®â! ŒÅ8sP#ŒŒã† ÿeF±”‚I•§ø?¡:jpäÓ/£¼}]ÑMçÏe¬ïTÍ-¥#¹—Õ‘ŽLâöú–e•œ•ý·ìÝé¡¶686®k(œg‘sWb={“΃~X3-r?kÂø㨇ÿ; »šâ6 æ×ÃôyE¡6É¿çs_{d‡’ C³Ý9¼œåt0£”©5ž\¥ [ô·MÈ~-¿î0¸‰'ÿy¦;!òŒë¡+¤[~žÓAu[ %ݘ¨n-‘Ž7Ujåèæ|‘œ5!WrØûM=oU &PÉ;e_®@‚؆ï§bÕxesfþ›÷Ëk0•»ä&ǹ¬6ºèB£añ® þÔí¢V<:µýã’eOëžg)ñ‰hapàõñþ‘3–¢ˆhÇ}Áñ·.ÁØ7'akØo5kÕéfšbçÃ9P0aD7ö÷£E؈ÕƘ`ó…Ñq"³>èw /ØG؈1axf<á%²xtú×â %bµ|e€ûÙþ@Wúyu„nzÈFkÕ…–橌C]Ï®h°@tN€¾gÎ`‡±8™c`s-^ጀà*ëH!p©Õh :Z×P .¢Ï?Té ³_LºCçJñÖì»n—@FÊ!?îc[ÎãävÛ¨·W8žf§à¨«»<¯.ñã0\2 ÑU)A'ÞÖ8‘ÃtÑqrË6ºÄdñ?&üª==Ë‚#ü{Õò±¡nMàƒ²Ïæ,EºÝŒž¹^¼ò^¿Ùp oŸD`ÍÅyiÖôpIOK¥âW•Y½ƒ>Üî {Qí”H­ÜHM\êxôÓK5©&7pÀœB˜Î˜(àdEÍ ¯›j‡ï pMÕ[Ü:¼õ¶~lºD'—ó°]½³Ôsn4Æ;¡·a™(¿ölgé›: âK˜­bÐ9´O¦H–Hú?_î1JÙ³®ã€Ëƒ’%ÌLJ*‡7sÄó¨€]Ü‘‘il]Ä<ëMã_H¤5bû49tR Žþ XMÉß–®ØÕ´.¼Ù¯ìn+ø‡<ÏŽ…B² <î¥Ëל€?mU ¶!¯¿Ë`YG¤4·,¤ ë)¿Jî‘&;™S°2ßux™áRœQqæ_„ŒÝý=æ®ÖŠë?Ìu¹=ù ÃIz¾K° ´IÒc2Áü€—†Ê‚ž&„Ñdhn]y/ç•MH| °Ø0~uåA¯ôèæT¶±Sﳃ4ä K¸ ý‰fK>¥=,ÆVÂï™ÃO™Ì‘J\žÀ.VD±Á„·/ËSÚª’¼‰‚Éè›Y÷ ýxY ï°逨Å)+E¨~ïJ¿Í‚dà>x{?ŒÃça7Å®Øø+ú’t|Ï-[Øš.ã™Bªß8º?ëUzTªåÑC!²ÍÇ——¤ù&8P?§‚ïÈ"&–'¯®Pí{ôÍÅÛr¦­VgzŠhîÎyË…Mi"â9òÐI?³%… 5Ü­v{Ä:ÜÄ7't¯ÃÌ…Vzh\"Ë –J¶{„¿eŤíÈœ  dÀ¼¥~·ÿ3kâÖ:={=~ èU`̬ÇÀ(-á oÔ|KY$™øYŽùjí_ªñª®pŽå 0™d,Ž;Ãd 1 §³Åع B––(ýÌzßX®¢ª3ŸK„%ò£8L¯‰6ø¢|Vµ­Ò_ÑT·ë­°š­ Ò MËÅwˆº®ÉE;’Ü¥xqšØo²?ܱÜã'-© zí†í´º†¬ˆ<5(#šÿ*œÝ .7$eÍØþ7UL,âf=ÌCÊö2² \vûèÛ™?öjžÚ)¶j󷾃P4Ï2çC#£Ù*$\i¨'ŠÚÈ ™x^×´¦[ã6ËkéÅeöÞZbk›»ÄgF‹Rr–U5•ìLÊçSô ¶³bDÒfP…Ýq<°R¡$89ݯkõXdz$øeuþQ®Im®6/ALô[ý§™g‡õI9ƒnÞ„eSáVíªlÙXüŠa1aUå[ãÍéý­ŒŸîkPaW»Kë¤8ÅrônP’Q«NÛÞµVêi<®nö%K ÍžoXrZÓݳ}”TÃúÅÕ˜'/!ÙG±çI†Œyr;¤ ŒÌ|*rnèÀ¦pt—hß$:–Ý1?oReÀÈÛ 2ÔV:U<è‘„¤YhÄž1Ö“›“æ§q@Ì ù iªÑö†û DÚ˜4ò,ç¬çooÚÄ,9&M 'yF·}«¯¬4趸ñC[!B©”*‘‡­% ™ùŸ…æ#j Õz†è@œ? dB?³.V³¶àêµ°^§0RÙ(¤d.ã6"½-ÀΔoFQŽ4à ââçÚmï8+Ì<þg‘¦Iå’˜Ê`¡«Õ#ÞÎ_$ˆË&qú)028°äHjlñÍCî4Ô:”¬›håB˜£ÈŸ'|¥¢Î#™0¥–ŠÓ÷Ó»œAÓrjd7„H³ÇÀRŠßÐ$P4ä`Ž:&†šªô>ÉUMÂîòIâ&÷@&¦ƒeFRKîß"üÕÏ‹À’ÇwìÊOëØãEÕž¥ ¼Ÿ·b˜ÜE´eMØê‡·?:x“µ~ÞஜþÖÊÚñþ0ásëÄUâ+¼N{íÉÓîú°ªÏ<ÕÛÞmÇÒªN݉óN¾~YK©¡Û[cwPûlŒxÓhÊ&pøóqw—p>'cúH…¸„…ud:#Þ§mÍ@*ˆµ6ǃn_9”½Y…‡&&‰8rU¡ "•˜Â Å&$Á¡UZï îz5„÷vKVû¥y~¬'«.Sû۶Ƨä¬56FJN¨÷LzÊœé:„ÒàX!»Íçÿ‡Ä7;_z _‹“X¤,¨"лî/mTLq©àF*Qzˆ’b»}ÑKN.æÑ _g’&¢ÆK“ZPধéÈý¬¤Öƒµ5ÁË­˜¯iR©Ëme½3Ù WÚéZxÛ¥1V·#;Ë?[tTÄÞX•UÜËQ¤˜R Û­)ôQÇkåap4ko{¥Æë†I˜Þþšz †ø$_çœ h$Äée›–·'ûü®{‹QDrš ‹]†Ìy@µþÑ}q°Â©Ï ØbqÉ{@n˲âvÛ9®vƒEݼÙK Þ¯&b1D}‰\¡ÞxâL¹‹ùW—Ô‡Å/ðŒ¨é>[a6²vÔ‡[e‘D®\Øq&ÝåØR…Q'ˆF)ÎMkç¤n:›ì>Q e Úñy‡¥yÎl©Hkj]"„'úgè×(ÑÛ™ôî#FÅß/Z΄ÐM¶ó4} NòKEÇu¬Å«˜%Å·j€¦|#ú³T°‡à-aÞ=~èV9»‘¡Òçh¸ô…Ÿ|mp4@ÚY£z‚㺴¢£ )ÒDý—ºvSl þ—jEŸ× Z@~½1òôÿ)x"€¬qˆ—³ŽÅÓkŒWpm¨Ø†š?™h¶ü‘â‹{Ê0Ø{MB¯G©¾z¤ª×¿¿êrŸâ•ç±î4ÒTo òÆ ZF Ó„ˆ;屃¾Â*ŠíNgxYbó˜ËÇd(©©wU'RØÚ{ÐŒiì«GIúâü:^bƆoi|†ÄØèéêÇQÑq¢³+áï 볤gUùA‘€¢p¡´—«.õ‘HB½ÏÁšhus­¨1X2[f©i w‹¶ŒlçÂé8wü¸ø•d€ Øž _™¹†ˆ™;Å}ÍŠš$ŒYŰPšµzˆT“–\í®ßòZrÛÏqaß…„4»í®ÀJâ™(«:0â@Õ£H\âÀ-5HÔ^¦ÿ£×Ò†~DãLÅR‡ï¾G<†¶Åòÿ§CHÔŒã9Ó`)»ùŽáWÐ×i2~/=¦Ĉ՟?D V:±‚¤ûòãS)Û8NŒ} »:ôÚálm.t°³‰–¸ ÍóÙ‡ãî¾åŒËƒAJ.P¢E(rÝ¿ˆ%AÈÇ!ú¾NÜ+ã„ë÷aò^Ë”™aIl"ËRèŠn´¬­©ývÃëSÎF ´NëoW…¬\nrxÀ*@f=½R×õ¶¯ú*VŸC¹”ÈA*$g3u–9UÉ¥Ú쌃FYS÷¬EÚ4ÞÙ].×gjx˜A'ýØ›lð¦ð‡d…Ì÷ß´F¦üe຾…ÆÇì/IPÐÃ^ôz¾s7â$>Žß›] ð|è¸û£¾†¯BŠîP‘^JÜü%‡fÕb^SGVÿ_o4…e§; FQ³9¦tLÈíƒÔHuü_øfº„UBoÐí`ætÿ}Ã55 }¨"öXºT=Ž4ÿ€Y›mÂiEÍÛC‰S<Œ»D½SðDx8º¤NÎ)n@ºÕÏú™2¢Àí7 g)Ç£‡òITPÆÍ]J0”Æ?b7ýäò±b_K{°“óûDü’DG]TSu08™l|‚ÀMÂNþIUÚ@dâøÕ¿/-f‘GÉË¿ýHÿT€‘(4ÐcòL°·d‹—ãáq©Ý8¿Î›Ww£]n è¹ÅuF÷ÜõìHæÈHºƒ3Q—zìiH'Ž„‘¿JeLQzÅGÝ¿®¾­…¿j º‘ „agJ7×.Ô2h’³“9B³šâìÑ”"òŒÎ•©cŠ‚B¾³ß=2,"Ü­—M¾²â@XÖÊr-­<«¶Ö¯dœ ûµûiè¢áÆ Û°”L´‹h*yLåÁÇÄ}àP–/oiÂÌŽ˜yc‡~wbc93ôÿ‹c[KQÛTqküˆœ£´㈃jGº½¢ëÙŸŒo™·+º'¸–ÍTl–6 ¶í€†Ëq½?¯t‘3㵬Ӹ=ÀìYã$~÷@‚]Ó®Kþ ºuÚœ©½®Øµ‹'©Þè €Óxô  ½÷ÈÄ™ìqv§†Û_OßÕØ~ëX‚üâÜÓp•ÀoÄÛÝ*"¡•áDƒ ©è&úq{A¿` -}K@”Lh~«¨öûLÐs¥>‹ñáŒpÁVsÉ×ÀD-å³<²…â)]©?ª#Ù¡¨K|Ÿ $0<Û®FÕ1¸8¤9?\>”'¾¶¿êÎ;B.áÿÆÿÿ²Jû(Ieܯ7ÓSUCæsy†&øýóJ˵,ÖŽy‘íĺØxe’1¨WÞ]ŠÔ´1i²4Ð9Š5 ¯É¤ˆ¬nÐê8$lŽñ:ê€:õ —Ùe—üx_ :ó]§¸ÿô@(tßÈýwÇRáÔû„Rÿ¦:‡\ØE¨¯¸ Ô‡=€9~Ã'•êS³]ÜÌ.ZÖÕøß÷5¹û wM ¥|ç‘É¡gùs5…o$àF¤³€È¯`YPiL1ÞŒ)·Ú´Í´ÐÓŸœŽg…….!Ò‚¯†q¶ Yl1[Ô%À¹fEM:5V;½5k”H7o â÷sŸÓ÷Ÿëé1X]à Õüóg¬ž/Ò¤@¦ÊÑFrú˜òï©rÜ—úºQp»ï°Í€™º¶2WùÐ?묱 7CqUðð3kÅ05â*]ü‰1¹•åU{¬©ù%~cæ Ȇ Š:—H!7B©`QÃ(md,ñ‚õÛª[/Ì¿e[P«à—Übežn?3ÎóaD:¬²ž7“+à ¹~4{dÐ6Ç :eDf8ÕFe£xV ù¤€{BÄÝ)³ì&f¡ö­û-ùé¯R7ýšÞì&4?HÏ{ÉM=(‘.mëL;h0kõ e_iÔƒ´‚Òuõi­:ù¦²ÝSun{Æ D´ìí®„ú…ÖpF‰Rz*3 ÏÀòhÚÊæì/rêZìÝÓe…ª#ÀŒ˜ÏùÔÅäûÇ£´Ÿ¸{ñÊ<¾Å"´% Ÿ+ÛMißü÷¨“×êj¶¤¡ârK+³ìúÏñ?—I =ÿ­oð[í.E*¬›µÚL:ÖµµÙvs‘\Ùæ’j-Ú™÷(á½ñªE52…o è9l ›«h"3ÍÿQÚ.ì¯wQ96Å“Sƒ4³mûË)ë‡g ­85guB * yLýX–"üƒ<)Ý[9ðñU«k9«,ÿégÑÚ„Q0]Õ9Å ±.GÕ¡FüÏ@¾E2tˆÚ~•$—L Ô®¢ÎhÒ~4¤†1©®Q>%V.Xfà¿^—]±+D2ǬqgñÈ1J…Fš—ËÙÝX£­•g™{hdAȼè÷’û Ò}Ñ—W“vzɰðÿÕî–¬}9pæO„ºÚPþ%1Z«KRòb'_ФC eV{Öò¡Ãÿ™µñ3èn9ÀWüZ³Í0KäñÈ#Š ýñÕµT=ðÀ‰ã'|––…†Õ²ÉÁ½/{~QÀW³= Çн¨U`»„èlS -YªüŽþn´¡ꆀ éy̤/ Ø8uÀnõ‡Cô?:LS£ Åy(ƒc;/« M—Pù´™8>&P–ßÈ®—EøÖ¢uôó|MÛ±n§Ã:é™ 5f¼­\/o¶±kž'IÁ”H¿%UZÓ^I6ª¬©„ƒ%Bðoó|PÎy¨>7^l6?ÙAtoâiÒ‘ÑäÑgþ€÷ˆÿärÐ2°!¸Ä’†ë¿æp•À ½ÍB˜rN¤|<«ohΑ ‹ u„ ·b묠‹"û‘!L§¢ 2?M"c¨wG~.ÍðpE8î½ P:ô ¢Íé_<çÕÚàh•" ‚YÓq{±ÍÝG®Ñ©ìëò Û€ÁÿåàØ¨× fcS8@4NEÝÞA¨ÁK@Äœ*_·a-ÅåÒY'¢lžpìÍÜëûø2÷bþ«¹}Úsÿh±÷\ì?ÿ€ d,Ê ’n´Ð‹±“óàS‘‰"O(Fº= tŠÙ´¬äú$Ùû¢ô%w¼MÝ*ÆÙÅue$y¥ók ¯¹Åôøè Ò• `İøTFj|š+Ðë, Gõ‰¾m?ƒj8GHŽöDö<‘€…•$MŒŒn$EJ¡=-°t2S O"Æt>8*SÝBÖ28¥#ñ/øÕe¶4 B;Ç ª °-J"n_®Å•­O¿A½ÇŸ}6‡ ›bâ_¹‡à•pœ Ð…3úÍØ–]d») 2à L({¦I09ž(4áå‹a©ü‡x¿¨7‚ÞÕ âaÖ¼%´Sisªæ%k74Ý‘í/ÒºôX&-%fMLڀɷ/€à–˜Â銚Â{e{|ò*Àª.-úPA‡3ñà›fbÒUÆ ]O1q%Ò—¢F+m‚|gy“‘õF”ikÃÖ#l7?_î46ÓÔ3ð6† +qìOÚýÈ“}P<|j¶±Â1Й¬5ç£ã¡iÙ»Pwûç±Øñ°É›Ù#Ô?»”Õ¿i0¡ÒDA½Ùy¶Ì±p[È„ýÆŒ§s>‘.-ž>VËUgEiUv:Ùç×O¡¬}Ë6ΪáxUf1 Ì[£©˜~cã:êÉ—¢žÒ(ä\<Ì[F?j÷¤ãD‡}åxÇ1 wÜpoÖB6„öëJß6ž>´ã½†ö`¦f„z‹ý´ ÁfÎU)úeNÿ®W_‹è…ì»Æ)´½l±±êÎ×2¤2»lR§A2&gÕŸC¸2uîÓ6;$^mÃÓ¾•‡¶À€qo #Æ 5r¢!úåÔz1ZPéNAYvóÄê¸ëëÄSkZÆ$Èá×¶læ6“v1†“6î’ⱦ°Ý‚Ä–fÎÎÞp=A×¾?K¸§ÁÂÿ'NÄœ6 º)ý¬ l uJõóBë”x^Vâ¼Ùõ›â¶‹ªîò|x… £·_2Ö89ú‘ì õ °†™¯z”N#Fc|u.ë)pŠmîOzx“{FÓyç`çãêÕgdá²+‚¤)‚â5[Gï|Qc¹Ë—¦ËêÏKmŠ=\æÄ=*L)fUÙÛýEz×ÝþA÷áSDB8qÕü9—+$‰›}O"V¸åü¨pj3Da•¾÷ôÂ#ÖŸ"huÕ­qZJÍ*ÄÈÅt Ë )ÆÌ˵«w¸Ì iQS¨Þx¡3l֢Ǵ$H+X"ªàƒè”DÁWŸÒ!¥kùŒ€ôÿêÑUeÞ±É4M>—RƒU¹bFÔìý à¶Å¶Rwiœ"È,̼ùx•¬y›}:Í_-­™€è~ñrô’P%2`8riHk"Z’'¢l²“› Â1ât–ô4ÙÄ- ´™Êߤ eeœ^7é+µÛëâ8Zo÷ÿ<ÉwZâM¿‚:£Ùï ´]FoïyeÅN¸å’CHï‚À3ó@ÿÙùÕ1O¿ áex i:ÝN)œ†’è©»Bˆ„Õpn*@­e-ˆa]䇻¿+…tg)\eêáö@Y·¢B± Y<¿› Hš8—:¸­°s.þ3ã1o— ËߊPDÐ@ˆ’ ¸i?¶¦Ê¹%GÒÇé<98†ªœç€€3‹UcØIćä))!×É ˆ{:â¼oZ«Ñ¤CO›Dt#Ã¥e¹ŠFsÇ•†ÉP£”LJEffž)céTTB¯ð+ÈlÇYø…H; Pîà¹HÁÇK%°}‚¶\ˆžøÆOõ ™ ôeг ü~MÙëL÷„Z¾Ôøc‹ø<ðT¾2,« Â…¦XnàJï´ñªóƒì¾·‹o=2'äUE¤BˆT Â32©¹v$hŽÃgÖµ+fs×ðPom —•›M÷gò:;?Ň²ôœä@`L-­žT¼»£_(¦³ZeF$§¹Þ1ëâ ¸_ȃ¹Øâ2寪_@Uõ<«$kÈ!:ÜG Íl2âçNIæ¬ù÷mÉ`8±{ª ¦ÿç¯ÝøÒ9ÛðÒCL‹±‚²|Ù«Ûr4EDSÅ’h%6º¬Ì”æ8¸]Á ,ÈÐI#£¼Á‡­fð\p~¡‚)Ì{ B|¶‚AŒŽö1µH뿸ËÃÙ½žóš0í¾@< vÞ‘ºÎ7_Oò0˜de'2–¶÷ÈÉ®Œ†š.õu]}ƒGÅ.m,üHüð½C¥ãA|ô"ƒèi«è|‚J+,³î&„ˆFBæÞ©²îgÇg2Š==b†ÃÜuh¸wE‰ˆ#ÞÌ D:. #@Þ‹ø9í7@ÌUzÙWÕ c4¢ÄjæÔ?lý¶2WvÀX š3Ÿ!#к (v ·~j M*s-ΧۂèÌSÙÚj’$…ÑÎhúj '‰¶kÖf¶îajåk—ÔÖäãvk­C]¼o–okÕä~-Ô-8ƒýÌÞ°¶&+Bám&[Tßrllè|é½k‚”s5pøøÓÔ6Îz…/]ŸÃq.¬&­jQ!Íë¤&à'Æ$´ ôZrÕvz¡PÆû( t¹ï›NTïfÃi£xÀ1²J?-šN©]Ї”¯¸_i­ôõ%Ñr× ¤\º@ù5þ ò ßDÅ%kst oŽÒàáMç%á þMSã}ùë—蔾a|ÈÒ:È­Ð_Sj¿8˜€íyšÉ þ”D_lÚCNOµMͳ´ ôkŽL‰¹À[ #÷úÿå6€ñÓ™8ù ú(q'_qàš7’ãÜëÍhs%{ ïOšRUÈßÓÉ[ó2MY_@eO9#íêV;×êyr¼á×¼©í*ýVŽ]±UI…µêl­i ¾ƒ‡úäboýO&çO’瓸1äÇ ùÝsmã+ˆáN¹»Zñx¬È< Wƒ:ÅTÅ*O~Mó{çÂæ[Á‹»)„áb—®õS½³Üs ìµu0‘‚ƒ4“¤±iãýùô]Be)‹~Õ¶Ý ³“UÖš}Àø.5±Y|ÝŒˆ"­JªYq´}¬4´”1¤¢!èá_HAO¬O…Q_Ë"rB~§^¬ÑHÿ‹kïK×ßÅq¾!æuÎYŠ&ÝåxÌAÛ~…{Ò%†÷$І(5!£öE¿S›cþ.g( ײE‹$-(áÔz Í9|óÜf·WpÀG-¬gú;*³Òétø åW1øïŸ[„.0ÚÔrI“æp–+í=NöY#Éø2˜…î¶’5.ù5Ä  Ò@²µ Îa¬°1{ßûþÙmBøs6®{UøUM~Q$6¿ÛµÄsÜ1OÈ5œôh}®rßÎʃƒ ¥pÐêû€ï"äºWÒBcK9ËFÛÕöƒ]¶,É·¶ôrPåùeý1€Èž=7S¯óô1ñ¤µïýÖn ¤Xm:—æ§£¦ÿUN¬¸7‰.úßå› ¹ ˜‚ðßP!b}i•+ÿs¸ÙÍÊB^ÿ\ŽO„K3Š,{^t2€qWÜ{Nj*Ñ5˜Ñ !æ|çK󫸮ÊÄ#s.ÄÛ‚¼ß†è-î¦ ¨ÚÇ€dKWŒFÏÑ©b½È±> (ËÊtB!ºwEpfþå_Ó¢ßH¬@_±b’ßv^FÒ‡Öï>Î5áÐîå¾wö—'îõ—5U._„qõ&÷ÕKñ Øô©OÔuÓ_c‰ÞÕÅqzÃó®q¤ë¯Åƒ‡òÒwˆé±Ík ëüB#ç3‚Çar¯äåXÓ“ÏòJ¥NÔJÂy´Dûµ¦¢%ÑšÿE7ëŽ6AýÆÂ‹²°lLË ßù[Ž;”ÒêÜòÇWŸNa‹Ï‹†àÖìGë¶R& üÎ|¸öÒ*Í1‰ÉîéÚ3“w幨¶ƒÙîìpÕfY R[‡Ê}ò#jO?h?‹Ë[1=ñ8€»kÒËÃï1¡C>Í­(·:›pPŽôz°…3±ÏÅÑ=陌§ÞÖ[Xi/Œ½)ÁÑt"aœÂ’Yœt0vHÔUbVª{`ýuX]€­+§˜yºkârЩ^¬B 8ÛšyÊQ ¤(§ ¿!c'1±t½ˆÀS=2è÷å˜ø¤äH¡\ãô²¯"/wÇQ^ž;AIüxgfS_’Ò«âØÄ="Ú:ÀB7u}:¦©nò2G{O‘%þÞºyÒoD0*ÄQx)ÖPäTZäAXèúj¨ÜeÙkŽ©$„/Jw“ßFp Ä—)3â&À>¿kâO/¡~Š;îÏbAØ”g¢Øûú_ÖõÍ£˜†ð°Óþ‰ ,·2È•Fð!GúÝÍk•óÖi+Íä 0Ö•$p(hÂæ*”tÑhÎÔ/”\¤õéuwȹ‘Hþ$}X’ BÝþŠÓÅýÓbatUÀÙß:z Üšè‰fá¶tãµxÙôÁÚìåï¡~ê¢9ÓPLCÁ*ÍÒ¿¶›+A]üˆÒ3Ðⓘö“~‰m¸­¼Äêß¡^˜RKÑq¡±4¾6h^ráu¡|eôÍÍ’Sýx܆ ô£•L=ÍäÓð2AfÔY/ÿð°¢Fà0uPÌq.á~Iµ…SR+—¤¶Ê&APËyNµE‘#À¢Š[*ŒS…Ò)ØÔZ¿d®ˆ9.`"8+³ÐFäòÇ60š ³6k ,ßAç¨óÖŠ¬p~Öëzòç÷ʲqSÙyšPö;\ªãÊÏp¡‰F+¼b²ê(=#žwûS(×èWо+î,GÞR,i‰næ ÐØPRɈ'iѼ"ê±Z»²•M þJSüd}ó'Ó•È!aæl¿¨>L4›öXG–Çdæ»utLÜ¿r@^†P&ŽPæ<{B3€Êæ´p[ÑxßáJWdÇ]¡^s,ÙñAÕE’“èÒÊÇ®ÌBâaéA0¿Åh ]û1-Û8­ öìH'm-BäÉ]Œzˆ ¶±í…V%ßþù¥…7ïð8ÆðÉÀ—”-µr“‡â­BÚï&þŒj‰ÒK ƒÚb ÍØuOÃc°:ƨ0sˆþv9C$Žb™n˜ì¶½!ùãÅT•ß•›¯ØaÅ+öþ„gûkÅé_ËîÛˆ­ß90mró¨Öt±W@_øÚ~9b– ±PIŠ›ÇINr`³QßbGC&FfšÜг?…JåzÏnôªpEÄ (%‡©À˜ðñeˆM˜ÂÔu2‹լ ø)ÔU•ÑX]ìâöÝÆÍ›$o5Hþmª¼ŒVh¾1wL¨°wénd2–‚4P—gΡm+­}»H•ä8b³0k§©ÃÌh³z,•/8SAƒÚ8S@ú–PŠéÚw :(¸BI¬ >'c¼Ä$4÷cgãŽÝIOø³Æø#WÃÎM-’›\fÄ„c“çö°y €/3AÃXõUU%”>ôúŒE”1v$ÏO<ÁðŸño ó“œáE›bÝæ¼*מ¹NÇãÜØÅ¢húâZY:7ÇêI+ke(7ÐôV Tྖû0 'n”`ñwèñ81ç[œgÄ‚òF­Ãl5&9ÞÍ)M’¾Uµ•ùðtÝg]e{»É×.Rv˜öS´X² ÓC¦ï_¸ÂÔÛWV£gœrýIVWo¨Ü<‰s’ídøÖ›*£9ÿaƒ¿©j BmÉùnÍÿ !ŽÍ:ÔòúC>Èa“‹>­è(„„Oo^çL>Ë'Ïÿ›µ q­±ì+F.’$‚…V-%Z|t‚í K—Âòõœ¼8Æ2¹â®xIj„…6Î<ö¸Êà)>Õ¥õ ÎÖ( æä¼:è8¢ûâ¯;o'§þ°o5Ü¥0Å3öC¦wV Rô4ãó2DÿŒ2-Š)ÜH<(ŸÄ•Ç íwi]Øß¯6%ïùË2-ŠàC6Ś訧¸Hº‘‹ÛÄVŒ,ez#·áþYÔD2‚óBçºkÐì.š & f˜ù$ú<â¼>\i‡Kº\€YÎ膻:ñïw˜‚­›ÿ¢óÐ(2ŒÍ^‚bðrtEæ€!:•‰g> Ù èß½²-hIu6bÖ.›Ð0ÙÄXqŸ«ŠÑ#„“&‚ΚáMµð>aä24åÏÅ&€@YêÞM~ò|p/à»ÎÚTvŠMý*ÖÝ@"þ2õsK¢Ïà ÷£g–nq…j˜ƒÊÀÙå<î%y+Å`¼°L¶RùíÕÖÆ‰1Wñ”€.L‚é&ÿ*A*é ×.Æn5¤ *“ ÿ»“CQL^é!ÊBBæ!’ŒVºs2Úây¢—ƒ-·~û”úZÒÛÖì¡Øf+Ce‚•eµ4“—2p|ä’2"#¿Î }é‘vøÔ×$¶mQR¨ÂLï“ÍÀáVy­2á;ÛS?ªšß㤊ZÒ)Ï\ƒÝ-m]PÉy+Šö¶±²Ц!q;yüC´ø0Žõ˜‰ýweÇ7y¿ÿGݗ˯÷}Aö=d¯ƒuHËžð=úîŒ$Ú[×? .0¶ø.ÀÎ@¡¾iâ3ÔÖ;À[µd#V¿€§|^%úÖ¸~ö>`÷aªJgBíi3å xÑÇO¡ ¢YãD’›xR,íº‡­ hA­¡¡étèC–bl+¶‹›È®É¶˜H ÌÙ‰× Œ×»vú Nã$—ÄÕ‰÷«]1q#NÛEÆhó¥/Âÿ9df¿@ VO„!{KEð¡Sþ5ÉU“½kµC„–Ž&Ë¢QЙ˜üs ñ›ë¬ÒªÞWÝ/’WøXT å±iðlþ‰=µ‘ô€UG0bYmJ52Q›-°°'".Ó(Œëý´×Ò÷¯ÿZO%i0<&\1¡Ñ–ñ‹ ‹Ï=j*®²šo/“ñ (ò`©FÉÓ«§óƒ…ßc[0Ýæ¼6é2¯Lö‹b°Îæ§Nº¦ÃS÷X³Á×°¢… Ës³½©qŸ)«yªMA„:›ïéz¾L\dŸÏ\0*ßá>ƒÔZ2ŽŒ&Y> Q sÕ G쇠{ý˜@:qwC¦ŽóýoF•UÔIÌXTê¦Î*^A×éIÝæíæƒÉçýëÐò[G…G(œÅÂÎk*Ï*¨Æ%óèÉ꼂 2Gµý#)ôÅ0ŠLÂWH¹*eáÜtoöH1T­µ]w,÷©d®­œ«~"ÁnOÓÿËB ÿÚQbŽ öÚ¼,¼TäÏÁz"{)³pRXmÜâ,Ú'¼ØgLÏÍxÔ‡¸*¥´ü°cÿ“/P /W /Ü€]ÀUª^³L×;ÆËÖþõ)Žp†AÂØðÒa×¹‡¦€ò‘ݧe8pm¨áÎÖ‹÷˜HÔ~+DO3!ÛW„DˆGÒ_L+ ò¶Ñ°‘»çòòãˆx Þ]¼+A}ÄÓUמ6|”ƒN^DÆNâgsЮ=J‚׿S®Eš&+©åÚë ,Ù ¨´Š|ˆUºÅ¯o9ÏKOm¾ûüëIËü¬’‘®pµVnšeøM`‹!G þPíû™ÑÞ‘ gNµªÉ€{«U¨H!(±üJ†¥4¸)L©@”}®=Þ^!A·x¬™ÿ¿ž³áJo/Œz•«Yüv“Tÿ˜óO’ÿïX9í.«qf2Ò}ò²_#ÂôàóVpô܇ ·ÀûàÿýÉW·Wy=y PLpý•ÿI‚»——ûù ¨lÖñÎø¶ý‡1KüÄ-µË&ï± 6Ý€[6 á–…¼[ò±ÇíUxrI ¨“~¬ jòínÞYÛÜæXC“eGÅÍ^ãhCóÌâ?÷ PÀßélE²Óõ'Áƒvá¼±ªép·2‚(‡ðºmO ÿ#;ª¼FªK¾%gS¾ÕZ÷HÈï°¥j#…«à¯<½‚”F¶¼¡Îo¶~Ý’pÈlÃÿ]ÎëBý³-1Øö3‘=Çnˆ¶GΖA2Õ¹:N½LáHzTçš-ã—„µòˆmo´{•IÀ.“M›帆ÚÙ‚¾Ëæ ˜iFšNëֶʧü¶Gciç.œÊ®/l³ÝÁhý˜¡ztýÕæAo`ÞϨ,G’AU£ù#QWÁ«æ®—çÍ“V¶æ8%ä}”WlråöÈ…ÒVR)Åhù¾JšÈc‡¹Ò§.Þ"ú™ŠEqxpÅ|­4@s6äHšœ G=køå»~­wéž?´õª&óóìUd Ak° ½ž¨’‰ìŒ/åbªbÄ ×èSÒáþül(}BɤÕ-æ9•Љi‡’QÉuÚË;娡~¨ðTgºSË€A­FÖõ4 ðNÑÁ †Þð-®H)AžÛT__”Ê4Lw« {/Egž.öÏ,Ê7´*fÓz÷gÓ˜*kuó…rn~]\ÓRzÚ~[Öµк]ߨ‡ „ü°oÄlË37°ò.‰5:bWTX÷”Òµ?÷g4ŠØi= q(rˆÓùëçòœ–6䨼ô’UŒG°°z×Jv‘ú8Z3ŸäSÕ˜±¥³¾¨áR¸XÇëÖ…£}úV+΂ðšNÄî÷ füªÂmŠ™E,~Z!Ѽ ~@ ‘ó3ÂDwDˆëé;㇆GÅL¾þPÇݹ#'ÌÌyT°RçäzÙ5®Ýgø™‡ÐŒþý«ç‚×&˜Ê¼æêúø|YÌmsþ ¿Q*¦¶›‰H‡¿n_MÛSª§ÏB…8»fÝTa<’Ê£IÙâC\ò~æÕ U‚ÀÀÍcïaá¬bmBòüpM•IÈï=¸0ºñÆË5‹¤mI‰YùÁýt¶n¸¦Žl‰5Ü.ÕS÷ýˆÖâ„Έ¸è9Ä.Ì +|BÃZæ„È˲àª5bÕz&Ž=ð²ÅiÊ¿þò7õРø9lœàÙ½y»1 à.ôB™( úEýڦοߦ…ʨ¢› ì"+ (µz}Gâ×–y& ·°£Ôn­{ˆG+•ÂRo×÷Ù9ÿÎ5 ¼cÛ»Ü3UË2K¬õø·Y”¬´l0·ï¿dè‚Þ^ó¶A°‰‘œõÔÞ}á'šêh¦zBs%EDñx³>óÕÄ·<+GãéWÀ;ÌÏ® áÈQ9YÂý“ÚµFmHZgJí‘kÖ ÚÖ,Iþ?À? JG*˜ f'?©¾®´"ßmåQÓNúÜž¿¢½ˆ…ÎMí§êÈž€±ìiµJm+ß̽àÓÝJjÊtL-û8{÷T^l”H‡ø{¡o­B°Æ)È7>³Ïѳu}×6ï4wýù$„Q¾¼"P`n1Öƒ„:Î_Ï8jtoÙ“]ÙÁgyr­K,R°¢’Žœ µQÓ©Ïk_—ûHkØÌrBé ¦ÑÍû 1ñ/cGh3Œ1“1© ¯ìüøõ'‹›Næ÷r‰hÇžpOA©Æ> Tñì,â. •2Uœ]í6‹IØj²:ûš,›<Õs\ûrHûgÎxÁ¼[6Sµy/ÃHqœ¿¨Ü’ù‹‡4Ñ.“ê‚Åj{N¤ºÔ*¢íÝa¬AÓþŽYSäÓdnŸÍþ¼q‘ævÉ_ú%Áø-ÍÂŽ#pª”ù‚óe"kˆðïidÒédŠI$¡=Œ¢»Y|I ÉzC«Ô\¨ñ½´Ç(†t>…ñòÉæyG¸— ®q8X­ˆë:Åoñž:­9›ôÙ‡=ä¥!ŒxO›y`üë˶2$ÝÈ>ÝŒÆ÷Äf›$[_óü.>EUÖ§l6ßD£²w€A÷zg;È[“3(·@ÜŽ—cº«ØÜÚ¹À¿l¥»Ž4^ïô7¿}÷õ ²çûó_ù i$©†•©RC£ÒÊÊì!†´,`Ú›8›S¦ž/<ŽŒÞ"÷|w¼Ò«z”¶ÐÔ)µóà ¿x ßÚ£x$âû˜Ü‰âA·-·:câ¶ð±ð«¾BÏŸÇNÿi©†Ü°’ vÏEöU»WyÛY*b\8“Ú1*yÈÉM"÷ΛFWkA©¬T¶ûBÞ3õ4ó™zr•PB+½4Vü2ñ F R-ìå-‡ŸX¢,.$Ì›ÛæÙû+TÎsV Á×SëëìAœW“B(RôqW¸ŸÅq<ÒŸ‚Bû«¸xCŠ,ÆøS¯Š Øg«Ew¨Ó©•†ÆfÆžýo6‚<˜‚Çî“ìNCÀ»…ê Çô´Þ¨ÿ¥Rƒlt„Äxté+F¡ÓŸú¥·rL¼¾ßâÒî!?ÿ«›¼n[èfLS¿9’ð?x` @³b¤àøNpåH·!nbŽ-~‰ÀýûÐiµ\Ö¾&ÀJjJMÀèLvíöXÆÏWp %Oø”ˆŽJEûÔZìŒò qbu¸öùEŽ©dûÑáWòÐØ<°ÄËU†¨ÕÈIªÔ{Êå3o·”@ˆSí—îíŒM£X†ZìK©åo{ì¾…äÍ'!“öV{h~¶†ºMžÇÀÆÈói#áù Ðz ˆ0ˆšu±PEÍ£a¡¿d#jÂ*}&…Űb”Š(Úðв‚ÌT© v3¨Âöù5óã[c³íyNûŽ'Ä‚äì$DÖ,Kg<ñ_òMð„¸ðm µe¹¶‹MMŽ1j‚âËÕѧª¿˜…ƒ3v×&™:0;uÂVÐ¥1&”) òÁÕKëf@!­>>ÊÝòÕ…0÷€º]ÚuTíOn1©†yQëTaÝü],¯J1ü]&ÐyïÒ}[üÇ+(ÛTdâ0aQÂVà;®ñˆ–iòG C–Ìz­r )ÿ¹Òmõi÷‚òƒÓ ³Ã8T*ª£þÜ"*S¡°jàèê«|dz;N ùÓ™DZ¦u”@ñÆwòÐ1 .b…U­»–àäûzpƒFHƒ7¶`º™Z]Â/x~†r{²DÅ8FyªXÀ:æåÂ2g„ÿÈÖ—!Ø@$ÛgÀ¿”ø5$Îú\’¦“ÕúÚßäƒ0v#3 ¬ÓWmVß §^ÆçÛm!õŸ6¬ÂBJ«t·cQ–:ƒ§ :IÔ1Å3”ˆ¬Ž\„ç˜v÷˜wûUGLTY+ÇÃCÀFn#ÕŶˆØ ÇébëÅu•F©ÚF×S–ðBsYs™PϤ,¾Á=—ý;],™¼ãî—z˜§'WÝ_iI•ƒ±ödWq1ïQnåÇ–’‹W4!.y«>à(<@=Á¸üÛ` ¥í ¯q‹á~"Ÿ)‚7ŠO«‘8pºç[(ª©ŠìÒYKÞÊ)i°L¬Š/x9=7ɸ¸<™ï€š“{‡¦‘†»ëW úœœ}L¼õoj&Ýy.3ôeŠÁ{êÉsÖì¾,4·/—€ê…›Pˆ¢Ñ±Ý=Ò2¶%sº¸ÀË…âHžæ¢v{ºQ"(j¹«Í²×ˆƒix_kæV>Òú Ìvçw÷FEËMtºwÝtåÝKÙRêV.p-žB: ÍY Ùó  |ä À»ýv”÷á=è Ü2gÏ&Âÿ¶Ô‘¢ÇìÓLI¯:µ)^NOŽnWúc¾À×ÄU¿¤ö²´WíjO¨¶L¦©6‹¶ƒ¹hÏ$4ˆ”½ª «!Õÿ”ïïY°`_•ÂñÄòƒO1¸)Þ‰ÚmÈÔøÙj½T±y}š˜F¾@±þ8 ?]¾Aê 7±A¶P÷T¤V©Ó6¡• ®9Š Vï /C¬×Ÿeímó Š¥ $ãÕ(ìá®:ÖôœXYžå  Õ⑸4["ýa&ÖG…bðæ.‘–¹ÙÙ Ö˜+{ ‘ˆüH÷Y èZu1ºDKtp^ª!WÜšö7[J.<›ËÒÀž—|YœS‡²Ê òÃ3q `ƒx–HÕÜ‚¾˜C)"ÀCû·kŒýfÏòˆå_„E ’€WÏ—¹22¿ù)¤È‡aÀc=NàŽu,¼Óòú…bâï ¨}ž¢üZD I$¬îA@œúòJø\×/Ì„»|K)»œvóŸí|—A1-К[…Åd"Œïê8ê1€EõÂ9'üJÿ'H¦Àá´OÈ©öâ,Hou̾R- [ümr\ó*ü¾q¯¯ öVA§’µfÏÍæŒúóþOá$ „Ò€»Q:i`Mîx V¶ ø"ÆF^”ܼä_>>—+6þ ‚4}´ka¹]gÕämqGx;ÊÝiC#4¥HÑ:Æ]'4ÕN†¾ß럘 ¼rávù¦`®ù>¶|‚Bî†Ô®îé“HS‡ÁÆ@;ßT*ñÒAµ¦Sv°Îs‰5% ›h&¢ÔaøÞ_ b»§í×|Ç95t]·Ó‡uüú†ÛFgªúÊøáÙjÎÏqb(’ CG;ƪD{¸¼·ìØä2R{†1Ë iû÷‰n½Îï?CâXž!oþŸ‘Nb^lîX“doÉ ú¥ZoU†½ü]Û^Ñ)¾µð󇹎Š-=<Ÿ£?n4¨Ò)W€l\ï-%Ÿ†kýʬ¦ùö$#SùSŸ*‹eœU½iƒ7At#…n’ÐÛ*ß(ÄŒÑÈwή•Fº›ØOÿ½=.H+‚Å Âr$~àj,–W#|0 ÛšE7ÂZÅÀTï-©b$¤ç—Ø …ôLãƒ¡È  YùFE¸©.ÇßÒØfß@-׃±ÞVÒöFP;ÓMÏs@N§éw¬)…ÔÒÏ8`‚Ev,ÑæX…sU‘: «õê4PTQè8O|ªÇ‘²Âå!ýz9àGZÖ±OÊ}¯;&æÓ*w¼ç¹E,PèÄN_Ç’_0£˜ÒŒå?Ü…Ç À¶: ýà›2ôŒH}†œ[ÁF?¶ TÈ€2rKJM-¸’Sã’úÆü‹ °{W½óÔ àO[AŽ©1~ \|Ú. ë½ÃO_éi¸4”&Ù1rýrHB^¿<“ùòN¨ fvª(2套 Dï<Çþî®.DÞÝ‹² uË×i: k]Þyø<.õ¿î׿@[—}µ-CQèºA !?tj>E•Yɸ5]dÑ ï§žÑÒAQœo` mÈCú"£ÀD¥—…X4é‘BZhÞ ×«+…Ý`òý&£V4û~‘qÿÌOš` ‚ûïû-x¾–¥F"ÀÏ„}ÓvóíQ{‚‚­µ›ØzZƒ´LÊ D`ZÔM¦Ô¬_½œÔbüß~œ 0Q‚Ö0z¬Ò?ß`ÔÊ­"‡îÁ·ØpŠÍ( ³°¼l€-µey™¯s¥²syk¿Îjiä·umâ~yl¬Û™ÄUlùBÂü¥í8@ß_¯„R+«¼ ¹ôÁ0é>'Z·G3Y4€2k°Z–Tú®_50ze ´d$àÁ$°ÁT¬Ëê•™¥É¿Æ¿“©³Çòô¥X¿,õòóf2ãRL…ê”jþ.9÷$ꀲ*wëÏ@¾”ËÇï@`Ã:qæ•ýJ¹2жˆ0-â‹ós 2¤/Ä+:ŒZE(n¯Bâí¶Þ¢m¥îë¨Ý­ ,/(sðïÅlbúZÍ‹÷…Œ+¬BI%/^>fßO³5‹%‹z–ø{ÅÒqC©×_)u·¢´_³¾Ò©.–Fvv•á›q+p³k¡äV~Yr?yÒ¹iÒ« 3÷/Pä—Š÷ ˜RîV0;ɸ h¶tdnÀƒòeµ!a;@®2ó3†sœ“ /ÛÊ ô½ZF0<Ó}µé­ò¹í?MÓ ÂmPGÀMj”KúMÍ&œ³(ð½ð2Šì>Yœ É uÄÁÈ•øÚ4÷Ëçç—\ÕœØù\ÏÌ“ƒ¶O¿H_‹&,ôàF·Q»G?Ê mQø³2ª"åø J•ªk8ðtŸ šºýp']m L¬ü5.^úí<øë ÓÅvŠWcÒ…Ç˃†gü—9‡pÞìû <â!çí¹¿/*Ûþ*±šž§qöqm ëDQJú§yW¬z‹ ªH<‚×ÔшÅ¢°öÔ±{âßÁÂì©\=f'Ô’sÍ-céÁ­%ˆ¢úš?gb¸x›ÌùIÌ3&µ7†¯Òp]0½P<*íôíVëóåO/ì{ü€D¤‚0zÔÊ´• 1-ç#»ôí‘Þ-à¿| íÎ(ú:g–ÁzØBpmªúÄf«Ç'Ãó÷az¸EXIMGN‚[‹­YÔ3Îå«fµþ×ðì±³Z‡øoÅŒJQEÍ׆€óø«œÝeh‡BΛòçíñ`´z8çäÉ‹¯¤-78ÀÜe³úi½0Øx5M- Ÿ~ÄO:àðþÔ˜³.ÔŽóÁzåâ%ü Ô÷R,’¥²½Un_I¼ñÓÝ´èÂÆHÕ¢ý^¬= æz>˜½ÈÓÇúÕ£%ÎÀ?¸æü§ò¨?ùQ€¶3qFcŠ«R³ÈJÔ¢5ÛõOçôëNÇ­ü–ÈÀxdꪧÒ(Guê“:þ^Ò‚j÷Ô&·œŠ³ÒXpòŸÝmtäáùHÂ"s}NC‘|ŠIÕ°ïÚþ5"—•ÆWO¾Ï†kñO¯"±îï±à¤<Ñkó‚6ÌòÈN&fvðbªc].â §~wuÎÛÎëMùFmµ(‹À‹Mêæ¦tL]Ë,oLèú.¸ ᬛ8ÅÖѦ1æHc%¡î^;¥€cwñÖŽt”§¯ŒFŠÜHYK/¬²—’é´€ÚÒÝ´îöó»¿a ò—ãéÐ$M}ŸÙ®Ã)WÂz²ÙLϯyôèÖnŠIhæ¸ç[%‰ê”°&)ÿz#ž§¸O hÊÝá*bɶ^qRqWÏã-h ú„K³Hãù%Õæ\’F8¹5ŠFc£¼‘®Ó“ ‹h/g™‘‡Š¯vìn¤°[ÄnÀ6f Ãr™>¡:Ê¡l׺dm„´Æóm³E •uñh‹\–Hôÿr}ðÒ”ϾЧÀ9Ó¬A²&rHŒeÍž®Œ+;£f’áx £æÚÌwúÕBq ­ejNŠ´…|u†ùÜ·°Îq™=º2m!zJÝDX0(Èžã¤Øø´µ8¨Üì–àw%ÖU(]±ˆÐ­fÅxgKÚᤅÙ;Nm“Wœ¾uVl6‰Ž˜ß1åø(_%kÍÒúùjSƒºë‰‹ò6èço´½㬰³=D¬~O2G‚áêáÙ°´ÅßCô›5Q;¼ù ºçËE0“vèŠÇ„á&Ï9pk¹|ÁRk?½xíŒ_C®3Ái>ÅÙk6·XüU“n©©ú6ÞÌ•ñlérK´xÝ>QJrj§.BíÚLU'㈖&OŒcîRß^¾Ï¼I(#~¦>VX5Dƒ'ä€ïK˵#SϬ±¦‘ªÅ4x©Éê©Äô|ܹ|$Á8z¡I,6teû-îiMò`áV­yJmH ¼h{LOÍxñ·ßÀ¹¢B’{øU^ªT”¸íÕÞß8Mjï;Ñ×­[<¥A€¬î]³å~;àò2ä´›ÿñ2–¢^ ¯µUÈ!¹_ä´çvn‚˜úÛwa0K 2Ò+àô“ˆT­>’€QöŒ‡€‡à¹1q€ }À[¦gŽãq1ÜF}hRsü7L†uäÇÝÑåÅ':>ï›Wu©óª8»Àšú0}Jœ(ìÍÃoBŽá3ûYnÿwÈÄh_—ð¡˜+÷~C‹Œ!¯‚’£×DÂà IÇË7`5*å.Ø&ÝÇwŽ4ÜáB|¿wq¤>¹cž¯¡ƒê’˜¬ŒF }Ÿ2b=”—¬r+ÎØgˆd͇/ðÝÞøî]c¯E³†OÎYÈP¯ã¯m‚þgp›G38E¯èa_™ £sÜÌN aíææ´T9Ü3rK{ ýÝîê¹EÕ­a2àõoÏß6ŠËö©`û%‰CcÞÛÀöqÐÖä3D@÷Õ®/¹!ÓSF·=û;î{C¨:ŸoxØÍÒx¨üB¶DBu°EXP€B—:kþ $jÞ1D¬YläÁXŽ%´‘´ž·¦ŽÊuWYÍÁÓ])²r…ÜUsžÜµ0±·/º¤Ð0=·äR΢%ÒÒ=Ýçá´¹­’•iD?j,m<»p?—³¢Ë7¢ÛÑœ¿¡Àº@]©·¤@^ŽÜÔ˜ž°Œ./ùF˜oo’‘- U ÌjšËSþî¡qýðÅm·%L±%LÜ|úPnØpH7|²{IæÜºØ¥=ó —Y.¯%n°1uÊl RV2…uµ¢·¢J«?‰à^N³2œÁZ„J¶3 áÑ…ÖÏ» /Ë»À‹ŸÚöò#0¾ŸÏBÓ æQs¯—&åÁaâL#Sh?Ÿ1iÉT‚‹‹ƒwÛz± ?KýÉ–ž¨ªìÓÔê®w=e•mßúcäÏ0+£=[dAc’T@ÀD¹ªµmAϘFëçÙW„g£‹€š\Z”e”°µ§¨ðþÓO¼¬U£Ãý]§®ÎÓ­€Å* ·ÝXïÁ¬ÖC~à"Ž C÷ ’Àrb*|׃¯"죫ÆG26DËeÛ]ê’¡F"OLœ'¢´ûg0´2ĵé飄Ç UA’ßÿ¸Ž€ÎLûjWà‚S¯l'à K€Q=~Ê>ÐÏÔ¾NW±Ø2åV[–?Xbs%L‚Î˳ò‚¶°?×öH5ɽ+H‘Ǭ밠iL$“éjÚûV ÈYªý™$G†‰Gê ð½èpÃy–zr855]L,ž~Púx'5:í„0wPÄ~G†‰ =Êw¾AÛÆaÌÎEyiÞ/ù¦S»öÚlâ°÷.EW]LeÐ:RY„~fŒãëÑëqOcWVÆÒ úÑôœöî'w¤f¬áé—†!—Î^ÚÑÚÒf†ÿŸ£¨!–OŸ)ƒ˜_øy{SÈlr 8 5ÂÕAØ ƒ‹Q0¨¿.ÛFQÅ¥ðÁµJ L°ñë7´r>X ›0s-ÑRC©a#Wgœ³NûSÂ[{x-eß8¡–«Çý|–x—Ñ&¥••GýJ ûejB{z\ZOH²1w$Å®= 5ˆ!͆N­’ótR­îõ]õP¸g1w*îñ¬‰›Ë}(JvòµJSF²œ¬æ?'`ÞÉÁrôŽÕ‡w«Ðïf(޼e-àíšR'Táùè—xû¢ØÚxÒq»àLòü@wWD£Ã:¸N¸:Fçtž39YÍ_~‰Ëêï†$g@>•4k°‹«Q»÷ Êž¦G+›ëc»›®¬¹—Lûgÿ†ê:Øb˜pñï]OÏ|¿R;öŽ£¬BjeÐXÓb>PÿÈÀù…èüíxwÕùû¤€H`-¬§Úëtݬñ*½ÎpÉë@ÑÍïæ¬ògŒð®C@s*ñ¥€¿÷ÍÁ0Jg´šë Õ0C&ÒÃîÛøÝùÕ¶€§|€:Ø®¤[ûsÆ¡rlL”ôõMì·Áè,^ðPš6qj½–¥‘ãõDðLµÏ6º¤ à­Z¡F˜î‚°Vð|Ö{E@ðõIS_p°X@³ÃI%úr(¶_îîäNæœ} a’ÊlvÑ]éN‹ëÈ8pßÉa|aN‡Ldël=û­$D6évé=ÎéŠfd¨¾yþ|Ÿ-¤ö¬ä•°¯O·'0…¬6˜{bóý 0©"*0ï¡Ó…D_¸@ãï ¢6 8µRÄT¹&••¯rðÓ;"Œ¢ýÂVôr– »¬@¤M5e,³ &:+È–Ì{¦ãÔP\¥X“ã¶'÷•»Aç9ŸôªB ÛÇHp•ÇówyvÍg{—‹ºãÒKôA܈nÑNµ*Fw×òœ!ñ-1AsƒcÊÂá@Çf•J>ß|µ\ÿoNlŸRus?š“\J~0ò™Û07÷ù;¸[|È&(£+ׇïIá[(ÊQá~„>îtþ–ü1*DÚМÚÖuDÊfËaÅoWÚˆÕlkFl|qÌEþñPèÔ¹{N÷;{Î6Ê==R… ¡Çü#j ·¥–:Á©¥ˆ†é„¯“*%E}Bõû!ø`.°JøµÒ,t*$yâõÆ8ƘÐà„Þü‰a†ûôoðAþ½ ‘!H°h¶~p–51=Gò§|ð$­&ëj«ˆ&u¨YvS—ßÀTñ"T9x³pè^õNgŽ-XÓ^¤G-­;¼éŸjuoVí[‘Ü”µ)òµv–÷Ûã¤D›B±¸¡ndU*Æ_T鯳¸V,&¤X>F䓱Ç_kÇeFu÷¨âÐ5~ºbw D6áŸc„Æâm¿’;ÄíX®½nÔ“95ßGÕzÑdy!vÉ{†Ý³«Z΂ÑèfãKÍG›aÃØ%” zßýYfï÷¨ ©uu6Óê ì½AI­h^¥`ÝÁú>ÕÁ{J–t…~‡P÷"C—7:|޾&Øj_ÚÕ4»mÆë‡rƱKqoÇŽ ¦Ñg"Ö~˜àÕÖBj¥z@¦à¥¥±'Ê¿øæ·Ù[Q=t†¼§F‡ßNciÞòi ©³_?¢X*&ºuµÌÀÔ.Œí^í¼s 'adGkc¯«+}PÃA]êA‘v´ê‚¤ßðßZ1öÇÓ1†" ¿‰oB§°Æw£ë-C}P鹿c|Í$ÿI`=ÍÅOéqs ’äVk] !É~óa€n+ÝÈ>Âð…Ðù¾‡@çÁ¨°Ò2Î@ÅÊBOÌ,œ"™:G8àjó¦í¿lÖÔ¯ÿ`Ђ¤“S,ˆ6ɋӌW?Íã~žú\Rñ~Ý5içÎèÔùOiiJYÇ.yÍ4ƒÄwílìl… Aé}†6»³­îòÂ28nŽQsôþÈ>‘ëçví¤à 3ÁÙ)Øz¢ŒÔ 4Ã}ÁO¶6øLåk©E·¡êG¿S¼†ÿ”xc¡oÄ%Peüáþos4³ ÄBÙj0nüžœŠG_[•U"øôESÉËVjß5a‰é ÒºcÐ’Òݸ};è¦AbI„Òô­öâƒáu†=ã)æŠÒB;¶wwÑïLó”GF—úÖçnÎóÄÜè“üÅ=¤Æ¨ î>›/Þk‚s{SôÏSÒœ™w›‘92&J¸¸D¯›çÞ_±ê:ÜIœ€Õ$–¥ ËOÊ‚Çò6™[+Ö¸ss #™KñâlMŽüÙ'W3烞tŽsç‰E¢¾ÿÜ$5Ft·Mlÿffß¶‰”†”åNÏ>VîfÕ8”Á)l¶íáMÜ×t2UØW’¿úúPOæZ}}«AÁYŸAöhN ú֙؂§W4.dz í¿'-î¯Ü2j†x˨ÆZz¼‘‚ÕòTÍ7ÍÃNßô÷O3€@d~öë •ZMÔMšFT/ ÌJWéži0ÇUy€ýÞ®H à•®JbØæi1EoÏç•SA ÈÀVE/¹NˆnÈ:8Ũ—ó¸u(Cöå ß Ô…÷Añ³g4}kÏÊÕ73˜4gSÊ´\þãïpï,/q7ÛîÑ-Î"oz§uÖ»ã€L°;Pú š™¨F“J½‰D~át¯ЯbõYlòH—ÏÃEà9ž@«‡’:ÄÝÞ—3_…†'ã5”á÷oŸtª,/Ä\ú—„¾p®à cý3Ü¿¼ë-@kj§stÛ‚Ýdk|Š:Qm1Ss”¡„ÞaIobø8@=HçÞ0„ST½‰]›Ö¬bR™þn™Pⵇx]4¢>6JŠ„7ÕDuÒ :™»s¼Kæ9ø´ƒºX)N•ÍøõLÇ 3‘ÊÌBWO­Ek¾Îâ¼ñǹ‰“!IS]5IíݬßÏøÕ%@»×}¬e>¬ûx;íè¥äï±#è&à]Ïâ–Ì} j„D„ãç{åÜ~Ô¾¦  ±‡Izƒ6ùö¹[«å í/;ÓQñï_Ôc¢²`‡¤,üx½¥[FX»Ru.¢MJ{º6g—D3E PO]òv·Nš…\Cé-9Ôlëþ ²ÒÂbù0BàÍ÷R³0 D\ÄÚYñ‚ûžD:~'BˆÅH@› þ)„\ÅìÐÌí¤÷8‹íQN;ŽË·–<Ö ñ½`aFC|‹ÐsWôN•›˜sSºEj¾?v&˜Œ ),_,H r;«=ÔÿS'Û¥Uø¿‡½vÔ|q;Qþ‘r;èî±)…}C¼¾°KXFÝÜX¤Ê¸µðñ\­}¾l/ü7­ôê24öšÅ§‡É’> ^$m»|Sp±$wà ý—–yYRšuãÿÐ^tŽäêúÁBžm9ážÏ„iÝW5Tç5EÀ@q Ç]08¼ÄDrÙöœ/wÊ›§æo»“j6´ç“uUO&ÌÛi(\nCÞ–Ò8Ôv„¾& X$Ø[Ôw+”zi5†«äöór©¼F±ºT¡óøªŸ.Ì æ"ò¸:ær¶"°PÃDš'VÂÆ¢1’}+WAyÄ XؤRþÊÙçRÇ‘¶o¥Û*)I{&³wÎ~ha Ð/é‚ë°]³‰)øWБT;¾2ŸWj&Ó9í2+ÂÈ7vGÞr†ûªâà†`È®ÆüÓp3ÑL²«U ·™ ÇÂêNV±®¾˜ýä‘LN¸¬pÖ{"ÙS5€¸ÚÛ þMïÏ%É ¶y§Î¥m¸ÙÕc­?çªZŽÀ#ë8¤S4y}dk ö6KM8NO‘qlj˜çß¼LÎŒõqcâonZòŠk3¼ëh¯S‚AÏ[r¯§¯PWL½[ §n°‘£EDRš7ÑŽk×âPÌ܆£Ða•¦yôúA·W ®ž×ïZV¨ÏŠÓª»#9!‰JJ)ˆW˜ŒÀŒtƒ4€›ZàNºœç©f˜ñð¬ï¦He3Ür§\P˜^åm¿ÕO„šu±%AXqò}%[<ñ¬ha|>ÐÓJLhQ§'Ô¯7+SæÉ@üwJ3>‡ÀŸnÛáÞW®(;ÁCKŠ’UºBòÒj`–®‡¤_z¬Æ¸ÒÓrÒb7¿q¡5ßÎã‘UïêAœ€ï)‰c7¹<²ö’]ª›)œÜïê9üŽú²äÿÔ•;öˆ‰ åï}e£;$ ý“¡‹ë”î8g(ì·š—FÐ&lËêÿÖÖ¦¡´æ| ®‹; _ü“Ñ ˜‚õ“EùÄnôÖ2¤ä&òZÈXî$†•c¸à«Ø0‡C‚œª=”dåXh12{©˜jѨòŸ® w,ØyôAÃp³±‚*óÒ½þ¬Ü’ÎÏ4@« ëèîß¿#D«ˆób`RïGèd¶sĘ#]_ŒË)|×%æ×7±ÌsÜ>-ð½)Þß;öü¼ÝÃn(kj2ŸéáÙóì÷qKt.š³Žøy_öï~éÂx@×8[^+œ~ØÇÝtºÊ 0(/ÓÍ^yÝ€>üCŒ/ÐÈØØû»w,á’Of=ÇqMK#ßês} '‚ ¹2žó ”Ý?ÀatUGu×Ö“ØÌH°õd£È”›OÖwö‚ÓL¾¹ ¸ªd¸üåIC—¥ þšQb‡‡Á–¯ˆÌ¢B;ÒÁ_"܇=b) ¥m„N³ ¶©>š½PzÁSZ›Â«]‹Î,ÆáJ&³X³õ^„›ÿ°¿ŒÒìIb.(ÍlUŸÖižp$ Êf,ÎÙ”òjâ-³vv|V ‘8gØ‹»ôrF6&½è¥F•´#6¼GÖgä:±ð ã˜9ß©Z~•kQ ò€5»`TM§[Ý¿km³¼S°fÄû cÂ4èÝ‘p󤂔¾ üä­’…þíĽ¦Ç]GF,<‘“ÉhóÜ=/ÏŠ›¡…Ve”'ÆX\{ojK™¹I3ÑYÜ3 œÆæu«5Fû °¼ù“éå,êJr®ñ™àF2'—— µ˜äDLAÀõb¿“hðm\ ɲa+aݘ™)£GU0tͪ£ú­Ã,Æ)MQ]w'œU8Ô»½G¶N7ÊðÌq¸˜•jcjÈ4À/®H^öçD®þ{ÿÈès$ Û¼^Í\Ú EÏ”nÄ„š]¨Tâ=>«ôc·ÁoC¢@¿C5nÂÉŒ<(Àã•AðtyÖÂGÄÙèn¶ð̸ç,uÿ/SAy>$‘á‚ÔXÎÕ†øöíçÐ ÊÔn."çS1Gat–ÎéÜü¤¸Á<¸Æ(Ýø¸¥éõ4^$\xÏ7Rܲ ÆðcY¯‚TÖó0© 4oð»Ÿél‰¦AµJØ:Ù®éY%fôýúŒPðXˆËÀùÎóãÎCðþë«üWûm–f›A GªwßDÇ» ¾§«™µó îX^yžÁäá•:ý<Ø©©Ææo„'öH<‚KSÉ—ïW±°t~[ß×÷³qÒæ³:ö›@^Â÷ofðߊòµâžkqSŽmÌ zv\%kçî`“2æÊ8}d$o¸t«Þ– ÒMM¾B÷C br𣓻ÆdËdÚ¦aœ$m;¹\êM^aŽÌâ‘Èøyü2Ósµï—û_ˆãÎ|Õ@ÀcJ?¥Çåò7Áò ‘ì(\[x_Vñ\ãcôˆ—\•ãPîW6Ašÿ vûªDEÅ~ñ¸rO·=QÛA´ÆÇeËéÈwþßåˆJœ‘]póHû~ñëâXн€0õ¿¾àQ‰©7“™–§¾ÍÎû‘`Š}·ö þ(mºÙ„LWνÏÈÒü#Ç+¶CÑNDê¨6×ôÛSO\¸úµÌc‡Ÿe[—ϯ1ÖHôñÁ[ý8^Ï)eHäᨎƒ8(œ…Aw\©u7LJ'&ykŸÁü’l€È`ÏzÊu úJÙ/¿•°4‹y%i$Jºìzrq…ΟÊ%®¬ðÀ¨IÆÕ_—÷Ìq8&¼höÊáÚþL>W—{XgŒÙq_Ló‡J%[`ƒæ ý=ÛÒ_ú@[£M4|--q9;° ”˜}@‚¸Ÿ9Ø¡mÃñ²nŸÛ(ˆ´"Ó[®Å¹î¢½'ìYÚîi¯ÛÝ=˜ú0-üš…´!ù§2#Щï£ÈSû¾em§ð0Êl& jní¤ómä–4ƒËFjBŽ|!&¯’Ù½ÅÎy.ž Œüƒ\¸—&˜* @Ù©õžÕJ>n4ä[6ËÈV Œ#é å¡,ist %¾`E-ý*S7¦ÞhÅÅ…ž\B]W¶…ù(H‘ƯTF3h8é2ûuAGö'uõf{pàXJ/–9ñ)¨iûV¦ûy5y¢É‹½`EŽsTETåð§qÍ»Bñ(§[A½:ÀO†ä3² Ù_y†åu'¾ÿ5"aŸîõS¯[PÐä7›oåîu±¹šؘmVb6F÷Lúa¬·°ÁÛ“Õ§|,´â+`IöY¨©¥“b³ô¹-ø õ. BtÏq©äÃÏ !\Ge&s6zPÍ2+p¸HM‘º$Õ³w;ü8¯Çò Î¹Õ náÆÆx´“Â@Ι¸­Z¯Ã¸$$È|‰ÜÀuk ñŽh1È‹Á¨§Ø¡°|#¤]BXXÐ f**Kt–'téÄ)(ÆPBÖ‡õ´i0GŒÇÀê ( AåÁ’ˆÕ A®cD_„’?`ˆs®¹§ŸfL3u^Dñ¨F |ìÿÿx¨Kû'îñ»!÷ŸÄÒ,¢]匕wO—¨i§« ûßsy¶:ã  }À=Yt–·å›ïëÜ °É€.U-pShR Ÿ›½vðÈk°­˜µYV×JÎIêç ’ão/yTˆÙ-!‡|yðpžÁ-ÓÏ_±€)Ÿöíf¸U¯ì‘²@)°7.º89¢€ËÌÚÖ1Ôoü+íû¥/K‰Ÿ*p\ÙyÒ!ÀUYÈr—ƒõNS\ÔŽP 5{L6õ¸Ôªç”(íøX†Òßúà#ž„òaY(Wöcñ2(ìâ#4c–ÃýÐè9-櫃IùxRñX)­€cW {!Ä:yι²+†[$'û3o7¿ø»/% 8œ”"Õ 3k—ç]¡ÒIqmî¤9ÅçÈúÜÍÄ“ò XÍüû<µA ¹ÒÕ oƺ–tÕ'µEªê,[ú ÚŠ  ‚:yRJ âÕS ¬¿?ÌHJÇõ;Ñàœé‡ÎåeMëVxéD¸>ž½Ÿšú–¨Å¬ëùãòSÐS2L$*R^WìBk^r]knÝݵùº$Uc–_1&YÜ,]Ô;çDöDùi.WBmÁn’˜—„¼9pŒX!³™Þ„Í;HžÃv#öÂW²x\¥è M¶’"bt\Xi$@w=ëª1„8琢£-~DAú©@p,aDxÄõ>40³7’™†þfN.Ö±Þ÷¡\‘á-§Ä‹ß”:ÒïÐÚL)©„ÕŒäæÛUºbéá¹Ъó Jj½3,Ê ýþùwÈ)0 Í%9á„%Ÿßý•ã%ÅðíÊî>”"P˜£<°½ïø®Åµ£áå"4çzJß”ÓBdÈФµ‰„ HÂë‡_ì¹~uiô×ñy2.£‘ ‘  n훲dV4ß bWƒ[Æ=Ï¢Šv=¿6ãö³çOþðíSgª[I­&«ÄØÆvßÕY“2¿—‘ŸËKlëýåj»l‚Å6Ø𸵒ÞÐ<53^,óeEÙ&]%Ô¶/Z¸´d8rb¢7gU00 >¥ÅGrúÄsËkT¦,{;ø›ÖLsœæ„cD ÷0IþŸÒúzJ¹1ør:î ç\Îî ˜Wá!]¯ wõõYBn®&f•ôrXõÿtPº2M$x¿"[‰¹aO…ô±‘£q|[YÀp¯Ãµ»m3•.¯6éÉ]a¬…õ~|U‡’ÓdÞ˜N˜ŽçÚ¼42`(W´U»,Á¤ØŠ©°ÂT?:ùˆTúâÁºÍ’u°mÇweÖv5ŠÛ‚sª(έíVQhÖ#´¾²$/Ę>ÀˆÀ‹Á—Mb§±”þ(0#C6, u€ƒ ²wþ=¢éƒð› ’.R5þlnèÝiHeê‡F+ÜÍ•³`tg6+ ôsÍ“?¹ê0 Q‹~—χ'÷žåÂ*2Ø“P0ƒÊÚ– ŒÚÜþ<^IL«Yßo¡4¼–õ‡…r¸’[ƼáD®J]bÏð=Ï?t«Ú4”õá;‘aCû'˜8°+³¶Á£ovq¬ÍM‘øc@ Ô•KÒÔP‹‰vwdôÅÍç_÷ºµm) ×¶t6s˜ÍÊ?my‰BBSZ–²Š+Ù:…–¨^”žÒ–JÀïüŽ·fþµDÄÄä€ÀbàÇÔñG¨©Öf_ÒŸÕ¦­+'PÊx³•f®!*‹“*€\Ó(ÊLœäÝP ¸½k–­ÉIÜàÇÎ3<¯û?™n¥à¿ôÖmcÌ©žs†ü0#ž*Éø´¼I-@ç¬a ­§^™Q\:6Ê` ®uá6æ»oì÷•XΡV ©¡âýùø ¦ÊwPÕN@À88ØLl´«žs2ºÊÒWO›8ü…ïm„8I¸1 Œ»Ï]þÆ9ºˆ#åž}1zTa­¢ j¢ÃáðÒXGç`’¶Œ5¡`öñA#U¿úƒ±*õ'Cåɦ'îŸ&ŒÓb‰ûu «<{ªvŽêÖÇ@õYm9䨥ٖ5a5zô;t$+sX²‰ 4b1¼ËôÅnëh6†ê&¹|œ˜¨6#O‘ÕB,%­¦â8,æÿø6AL)Q~¦4{ÊîÁãOÉ•b¨þ9ý—ºwáÐA• Ž<+8ób±dÏÙ¥Ù{Š/uËMðŸ9Rr¢ÿ*[é {þ §Ä¾³‹€ȳÄë=R’0ƒZ…$9'ŒóW·<ˆY:J©$ªvø'¢âA.Ù‚£ëùã÷azÿtàùù¥@Ê%pøHÆ 8°¯â?zΆ2Nñ ‹L07g¢'³ñJ¼ÉÀbxÊî$-QSÓý\¢=Œ › bòXu¡%+Àí²Çû’ŽJºÉñË|Ù%íÊ2òoŸ;yÊJXRK¨Ì蕾ÙÞƒ>¤¾C•Yi.¹g`!ýã‰î¦žEv7Z“LÀÉTE:¦M}ô?°Êé0xhXºÏís, n~,dÖÐU}°Oúu Ó‹(ü‰—.U²•Ièû:%âܰ>±RÇK[KÕXÈxó°ZË+ôâv¿4^×}š0E2‰¥è;°»:ý—V·ÙÅ>#6—NöÃ’ÉÞ¾õúmı„]MÀ°²Àõp°‡fdËœœ½!xÓ›mT'¡gé¶“Ñ#(S¿T©ðÐÉj6‰ì¿åqmjp%§ú˜ßY§–Œ¸»Î°÷EPÊÃ.èðo‰ºTªîw+à€õ¨LµÍh›|°Ï»·©ßßÝ[Ñ2q5€Ó+З”!Pv';]%½Pþ—"9>_ù–Õ´—åW}—²'ð¥|öZàÍx4mV$žB¾LØ“K<åîu{Ù·ã&2š¤õ?cs°j2MÒ‚|ä‘ÏïžìN'áïí´ç.–ŒªZ`këyP {Ú÷Ê—Ð~ò×÷¶£úD6ó²´€K*ëg¡¹WÂΩ@åfz~S˜í¨ížh²¨ù%ޤsáaÛrßÍhóÖñ¿ls• ëJO ;â:'™á ¡Ø¨&%búo‰Ý_š ÍîlN ƒëÓº¿‰Ï|{Ùæš½-§Žß£ö`•æ°†w :úª9‚3¤äß` NI«p©¼â‘˜ÿB$ƒh7¾×l„½bQl5•5E“/ÝóFëЪåT}%‚“t˜e&–ÜzÆZEÜâÿM•`È CÀ¹|ép&k6'Uúf£ «·Ê%ÙôŸX1\ÇÖkžoS%¸‡IsÔÀïB;5|§óºJ +ËÒÈ×Üõl\8Ç…Û çSÊçýýQ—²Zšˆ‰·Íðâçß¿èVSþJ—”:Ë, ;(.Ï9Ä;pEö;v/ª?‚JxíïÜ)‚¾plkw3§á=‚.(!*2¢J3E à ÈÀ5´“wvõ`ÛŒOR •^UÆý2I]¡ÜzF\l°QÌ´:º£U’ѪÚ;E¡Uiﺆ6~¸cš›{áaï|'(DŒ¾"ùiŽá/%³¹Õýí>«û!Á*'TiqÍŒÁT÷ÝêV“wÑä©©êG:¢ÉÆFï$úÎeøl먽<¡å3Éc¨3r.”…àû88O—;ÖÀ…\0Tßö*L†Ô!}î‘@#´€0âuTðª[þAV“ ʇ§Å<jyzýÚ·pñþ#¹»¿½»ž˜óûü_…ÛÐ=5rÌ>í›w?Ä=§ sN¿d¢ “.¥s}“†kÑÙ…$C&Ž‚ËQVHŸ¤¥ nÌ ƒp&uŇ‚9ñ.³åB~7ý±éØü?Å_\ÂbÊ”÷‚HEEh¦{ŸHµöo«òŒ-¢jG>ªSMã' ÓÜ‚”8,=VË}:—Ÿ*:<â0Š®¬7+¯2àUÔÓ4ɱJ£â‡YÌÑÏ>MÞ_ù4Ç 9‰¸BB.¢½Ð±|ß %‘Ý\X+N :„0¼¿óDå'`n¦=opI+ZÌÃxÛÖsÊ¯ŠØlͽ풅¬®Zô-öHþ˜ûÀ‘%ÔŒÒ#ömï8Œ‹ÀÓ9×$æå¹Q$aÜ, ¦W£WP¶Ï›¶ŽdŸ ?z®|elÇð}Ijõr½Ýg±òLÉ °®;‰ ×Cd.™Q;C!J }Ë:’ÞÕv-§5qo4Ôr0Ovþ†×¯aùp¬×·óô Ù`(ÏAö¨ÿ¤9Ç£ËÜ3ÕVÕÌÊÿƒö«‰ùüè´ÖC`æë$ÓÔ¿ƒ¤LL´ŒÇB¦:THª3Êð%Ðl"¨·ª;ùÚ¾ªxÛœpÔ¹S¸¬W»À«¯so@ Rü˜|¨‘óˆEÚl®|J?§ýéà%È,ɱðņ֢kâî˜Ó-¸xñ°šéa®¼Ã©m ,a[{"±vbl¯Bé+¦2ÀʾC4½^uÈÂR…ê›ÅÿugÍ0%Ÿ;B‹§tý%ï>®Œ4–Ÿi®y®g!…º ­8ÎÆÀ{³3cãw¡Çºñ†² 2ϳÖêüKæC gªq­òM®Á.þÿ¸Ð¡®¨3p¢ë ÊîÆÌ#:G|ˆÖŽ”\…¤P§€×F÷¬ø§F£LÐÌà"q-JÈܘ´¾<$C~ÊÑ€­é)m CM›Òc뜦0ÝŽÛ»÷>éŸ0göîi<.Þ#z†Š—3t£ÇÆ{©Hãá.Œ%É(®Fz]ô—¬Y‘cËC¥¤_†°Q÷xë΋m»XŸÙYÛ §M]¼ö½pGÈç’‘î[Ù:-Š~ÝIŒ°U_ŠO|ˆ%‡‹ßI´àVS@“ #X˜S¶³õ§<ЇHœ^ÂLM#Öç¦j¤NdÄ>‹é•ºð“Äõƒæñø|—*ÂçÀÀ]÷?@UI¦A[V]ẖŽÏz=$FôÜnòÞ“ids®œ~pŸ`M†²í™ò“y²ü¨Jßµé¯2c¾·&DKÍ0é#´Kú@¾ÿSîÅiŒŠ’"¶ƒNðCÿ†×·ocfKœe§]SžnFÒµ Åû^‰’¾]1Í3t¸®·V¸·P7Jk9ŽûAgÖ `œH –dG Ôé§Šn\°š¸ŸGij†âìEixÂÖ=Àã òh)øœý±¤Ïñ¦áó æb¢c¿Þ Oµ!¼5_DЙqäní»îö©}ïs›mox3 èë Ó–¦ºA`Ö*@}ÇÐnöD¯Ï3¨íï/6Ç͹ A´°X†·¿‹O¾3È%îq-qm=C^WÅ_9˜mgÓùïÂy4DOTž< zâ bÕö|³å5ÊŠs±úi ƒË°: (|…Ú†ò’%êQ®€[÷/^ЀÃc{ƒÈ#Ç $g¯üE}Ü/uÿí>(ƒíÓëû¼ ^åU(›$ߟ±…¬Á[¿¿Žz/€+,‘C1®˜±ÍJÊq½¹ÎÌWí þñ‹•Z_ L´ššƒ hò1çƒÛÉÉúzª›³pß™B‹ÿ9ï‚]¿…;9%Ò¥nN—‡„Ç6_2ɾ«íB¿½»6™Æ/€š®{˜Ô&ÞæyØb×m˜Æ7àT”f.Œhì?v*«/,.ðZú6 ‚ì+Ó–p%áÚ¸f§(^F5²N#,ŸJ>ݲ³××D4&-€¾ÄðoçÀÔ(Îo´ïKw¨ÛO–Oï—Öc[H÷­Iív'™†tº\ß9PöP£YÅÔ9›9LúòúJÞ5о@öžj‡È—Zß*Îåþ¶îŸDO+Ry 5­‚½TÜ%οdÂÿ|ç?.[‘î4*á!¹±µüg¨êIeÕB“ýPÝöïzë=Ý_bÐIg8Ðñüø[msƒskl+ž"`QW“ÄtéÕÆ®@ú—F“ 2ØEÍWSìŽwö×/³J@w¸P覣f…‹!€QP'ùùs”÷£¾…8‡TõÛ ¦hÀôo!;'±»Ø»H¼V*¥Ý·ärCã‹eh \¦1Ö)ŽS‘wλ‡H×f“Ųz§ŒvnÛùP1§â¯z×˽Þv"(™´¾)¾ÎWwÀ" |:iàB®û¸ä’}È$òÓq*óŒ!¸F²K;ô×*¨Ü3&°ÌG9§qi EPc‚`çñ¶‡7z·”Y‚‘w_´ã©ó8ærw~×6±NŠÏ21ZI FΡÒ$À7ÙPËÿÙN9R¦Âðå[m0 Ï-(Ì Þ’ªNÕ?0¦%£ûKºDù*ÊB³L¡ª¹¬î!– =¹gé1d„¼J/¦)d„x¿·:†=„ÏôD“ùD*ЩÏð®ú?ø´ÑH OÕ$ˆa`àÚ£»œ:¦«UÆuñôÕü9Ô'yº² ¬¾òÝcËb£Èéâ²é=H?÷$i ’ÕƒCFïÃÌS®ÒsÜ­Ç×4 > xþvøÏ÷p]3c‹ÑŽÉC[Ñë™LÌrYWÃp†3žÿ.ü„7bœò¯ÖÒ-lãeìO4°þDçü€ æª£0™†/’•°â÷w9ç{y¶ÿ¹ýê¬n 5B2WöÞ¢òø•€+ÞDcÞ›¥ ÓàÀ)dMì@ƒO²å‹é;®rpÝø¾æÏãßîv.1l€Uqk{`ÎÈ÷y=è§Þxî}çc>eÌj“*DÀUÌ¢·&tõÞ0í§J2ÃnˆT`9: [°YŸz¦z°¢›ÔÛç‰Ò2œ¡³2$ú¶Á™PÆ=ñ‹u—\·UÝ/4ÙËí¹¸p´$(¨jÏŽï³ø&¦."ULÚwofóórcäúPLꃞA€ê)x{;öª„ÇK©SÁI²yÀãA%ÄêC"r[¶Ó"|{UCí‚!MøEm:#»ô$<&ÚR°¡$”cw¢•;(í~ÞÿÑíx/®‹²«rñÑB€ØWàïïźuôg'…´ û´ò ÿ/ŸÿËšš²BvÈ‚¯ìp8µ˜f»½^iN€ö vBiž*=‰=xi.*X;Ñ©{×Ò ™×ÝQ%6†—ÓYºns”šß†`ªKž'  ˆ¨N4¢Ë1Ú/5[-8Å:Rߘӓ‰¶Â<Ñyvm¬;"œ«¸_0Þs±ßM ÂëbÏtt¨ÍœõBÀÕ [’†›œ‹J!ÛÒPòÅòadjo‚µÌBäõß玎QÙB¶ÞLADpyA¥”;HF»ªö1jL¶Ta9"²¿â2¾6¿‡³mb« y-g¨åTá¯ÓcçlŠ’Ò3³J!}HQõLdå›ó$%±ûøÑ¾Uíx·saj·­KÇ©ìjÕ’\÷×àïœm\iÇ"­¼º%\˦íîÔ¸ž˜Ð¸À\äbG÷æÌ:Ó–º'ƒ<½àº5=[jÄÜa3º†õ¿ûW|±[u-¥ÞíMÛy¼µ¤¥–€a¹4bä—·ö¬ ¾þ×÷I˜ÖçM~ ¦Yów|ù-=W"¶l¢â=Ë#"3°%6 ;“žr”¹4=vÐu¶&ä=vU·jîi|èv MöÿÚ˾"‡U-7?ÑS¹Âºd‰M ÉæÀ&é©sûÐ"·fP¢æI[©1»fôÜB\`*y#xu¢›rŸD²š0+±¶c…¹yå òT.|v¿®¶ÂP%¾QÀþ[€¿¨DY=;J—0mm,¨˜y\Há/)âLñ©‰È6’àô‘…3îÀ<ƒ}èñã˜ãÓbi¶ýe,tm-òqÓ‰Ú#O0ò…Ò°Ê|@)ã'߃½ sÎ4Ю*äºäU‹cð÷û]ù´õ€T /Ý„(KÙmÆrì?@ =3aaIõŸŒ{>ÑÕûÌÅ¿UQTZw—ù¢òSÁïDe†ÇøÓ§iÞh¥®¸ªþHý£]L£, ¦-IQÐåÝÈöã‰ß¶£g[–rMQ]Å+VèÒU}ø}æ¾PíflúÞÅHŽbš9,-ߤŒM8¿¦91£g™p|<É×çä°šTf™m>.РѨ†&P†·F†š ÐΆòO±ËwóP%—Hˆ?£éíÇ kä”9w' kõRì:#ÒÇ@žÞ.÷•g/¿"µá.¸tèÙt—êWpMÍW9(‰~W+½–f(‰_5".~!´ £°9j²óa¤Qˆ­Æ (,5¸dØa´°iVp÷Z‘È< )Ôj=ƒxB: ™­žüø± “£ÑŒá^ve™Ùç¨íCêÇ‚4•)EìEŸ2ÆR¼²Ÿ—ášo.WpKg{^¿˜%àfpðÌÝ—·ò—‡P õzQd$0e· µ€ÄÈ^˜wÖº‹vz°š?N3ñÕ $óæ$³}ß{>‹–myø™Ÿ>\ÕpÂÞÿÌä,­œ?¸,!‘¯ÃZ'KEû24õsR[¢Äl—38ŒöœþÍ\‹Ã<æ„ @Öy!@T|4½y¬×h’H¤¿zâCüa ­XÄc'„¿}?bªíG´ÍÙpgx´ú]Í’O¾¯‰`v€. •k‘véxâèB]ó&AhÃc᫵Ð~c)3–Áüœ(}³4)ŸX õÓkŠOy$€ÐHôfo²­„M`µR½¨wY±Ú+›QªºØ¼·AîxÉߢ)2¹qNê¾qØ5ÕW3Ûk€ßé×Vô’I¼¤\:ë ­¦1¬`~‡`Á<ÉK¿Å*¥6dðñ€#)S´aʇú5N²±Y`ô>—üµ@“vC—Œ’í3¯,ÍZ[ü§Gejí¸ }&LVh3/‘´›_üówWyF•œ0Ì›Xëà:|2÷cR_¦Ç<ÔòÉ Ì~WCšáÌì‹$¬ô)~@òütâZ^/5×êø"x¼úßTõµ‹æqÖ&"h3]ëdôJâk°<±Õ(×ÓíD´—óWÓýßÉáæé„œÞ¿ä$U·£D‚Ý&ÌŽÞæt[;Ôëè8˜ Ń÷A_(wH¹´ÞÐ<ñF®'úkØžGù‚·sž‰åNI¦çåGÞ0BPð§à¥ê´âê,+pV[]ö'O6?°­Ïã4Cñ) áϯ°ÈÑœÓW':²4aÜñÇ\s)$JY´Ÿ%ÆÀ$”šÆ¡ÚêåW8o¹½,wP#”¿l×=Ôt2à#6Û5bU¤Òr®’±†é¸Õ3#ÿÊÔS¶wa7~ªóþØblsDI¤µ¡…N3b¥ùÑùok_y­®†@¬—ˆ¹]šþ¶ÚýŒºH2–×X‚*ø$ˆ “ÜÁû².…:'´5il…á‡Ì„¸Ø¹<^dˆ˜(ãÙ¯žÛ%†Êç„Uf!ʸ|gT•V“êð8Þ&@¢ZŒaš×’¶ ÕÔj|•¾šÊEè͇ËX©¡ÜÂSófN›®3•™i^Ï^¯j …‰—=0òËi½(s<œÛè,A:T=…Ø`R«×ºV*‡NyÁM©£ÍÀsòÍw>¦ÄÅØ½q|d½Yc–só•(kž¸)MÁášn‰,÷eD@“øA13‡¼ÉáÚ0 .ûÖ2çu “¾ùH€ò‹ ½µó€ŽŒ…rid†Y©³Rh©™ƒ”•Z¥ÒyY„KJóD"ò`$Áæä |æ_/“ ¾¨ZÝ#\W£ Ć”à»‡]“ý1¡è–ÅbD¦&B~ºQ:ز„ã !û<´ý´i1‰ÉÇá8aˆØ×ê»VÀà’‡Ïpµî/d;¤å©¿}ô[©+²_< Íw?òÿ[ü^!­ÛðÚ5†¨2vE`µqÙk>—ȶ¨a» d‘~vçùTë—êŒè>Œr^ºøQÜú.“cûO>„›™z,Ðéñ±Îå¿^C ¤p¾[È-¾à¤±ô§¼8O¹gؘvÔ)vI„•»òE´tÍù_/ØPÞž¥TJ?<-Ç·‡%ØtÌ–ˆ…£ÿøTý©Ûœ7dQ³Q5ó>#ÖQ½ö±=ɺí|±BOÜnã~-¾c%\±»ø„+ÿ´×'iW£ÝkgjçzÅ;(—ïçþ<;dü‹Ì¸a?¢üa*v9êä‘ëæ ,$ö­}òFû½çpë[T”ì¡Ã7–†œ˜ßl%ô^@sk¤R¤ƒ}J¬·Mz"'NÑó‘"o£ã§¾¨n ØP„zñqsC³±DïøøÕÈÃA¶'´Š5] °©y†ôƒÞ³r8@cM–(Üš7öÔBK-¡Æž4DÙGj_¿;=5ò ³‰5ßCà ÞnaQŽÿkUÎD¾ºTKöa1®çÍ~áK0¿ÑPg>sô†U‰‚±{P•™‡æ‡„QR‡>ßðÚxáò>,±Ï»j –¬YMšÙ¨Öï“Ë€‹}™gI–@´ŸQ%A†„×ÊÌ %Í@4PÓPcœ¨â3o êÃJ/@8  0[y{déÀáÉEMZUÙ©Uí½E0p¼ÅXQil§å¶G ±5Kröe쌿,+Ñd†ÊÚ„Ï#‡è±î¯F’ц²pœ ”Êcf× Ëoúû¿µˆ%¿ r‡hðèïm*ÛšüR‰( Ut‘¢RþðÁäUÁl­¥3OIW·Nóòÿê¶ðmº´â™“›ÄÚµn‹€Ÿ–¡Ô2ô›ˆ ãô`m=ФëcfY­DÀèß Šz4CÝp&ßiˆ 1 ÷±E¨d,r F4g³›X†Å)d5¢‰ãüüh©`ì W+̰ÅlIó4“l èðDN1I¬èðtµŒ§8míPH² jÇâhœ$ÂQ+Lü8_HQªM ~¼Ròµíf~bn9|%0Â\ïPÄo-Ü˧ìÚq•u€V¤G²`h¿Ž¹àÊÌ—Éï±K»P· ¾Ú÷1ݯP&ÓàNÍ×—÷, 2.“tK&¸Ô·¥Cõº'jhpŠ×Y¨øWEõÝgSŒ:åOBí¦ÿs-Äú‹¤Ù?˜çè‡'Âñ q!ÊÓ¸myCG×ï~ÖÔ¿ë‚áå&¸eî><þºŠ á/2P‘ÌS5kñ.D™¼]Ü¡<õR¦¹ÁËLtùm™ò•Ô:hß_ׄ[û„Ë –ÑC4 lÏöl$˜¡>„,¸%Ui0aR—^-\Ÿ)öÖž«Ž»¼Púo)¢ŽvoGvˆE­Ḯ㬥q0Øñ;žüï>Òš¸ûwEFÓùlÉDó?ÔpÖK xy?€(‡£°Ø#‰ë$:ÌhY¥k N4Bùð|)ÅšþÓ.Áì~Ê£èn¿ïèå‚ü%(È Æ‚ìç›ÐHü­|AàŸÙT‰éÁ¾Ê‰ñ~>9“dÔ¢¶m†´€Y./IùËȉ•wI½Žð [ÙßUï²v@GÄYôój—tÏ ÍQ~jÑÏž ¹AhL_;ÉDgÉÆŠ´Œ3ÿ-?é‰Mc|j1ši‹îPÔ㪠iT¶ ‚p5¾K†o‡ ŠÙq©[¥qº½_jñ1¥ÍU^Æ£)E£Òÿ½&Å 7k^G²‰¯@<½±<4w6—‚MWZÐ$Ö@‚±ú4Ø{7s°nÄ1ÆìŸCYÙÖ6u_ ¶"úë9±¹ƒäþ©Õ>"Gºó¼ã^³šXH4…“ªï†`&ó J&o€ã¦’›{ d^0¾¡TøoCÆ=eÌj‚ÀY¸ZÚé˜~‚bS:¤Wˆ:7»lœ·[IœJ'2”ÿe–©w÷`¡×¥‚dd\<@µT‰y/Ú×m€G2‹N™@)jšÕ±Ÿs^ÚÞ0ç„]÷Þ¯<ÞØ™ç#”öX¦ÿ—g–ÚS!·)« bgÞ ùäz×LþÂèèh|£â»^7Ã(ª±˜Y C½µœìmßàNÅ&е³—ù±š<øi6ÛªûgMÖ ¥»n¼m¡¢­i‚ŸÃäàîU?ÛFÜ*[…ñ”¸|Ò[9 tÐöznbÍÕHÍSr@Ýûn t5ºØ¦`=È*:åôú4}¦ç2Î7 x¹#«¸”Ž[Ø^åC5pVc¿‡€ðÕPÝ»(—û¹§…äQ"ÄÕ%´`‰ìÚi¶¸a°Ç¥eñºm’‚]¤x _Cd*Ë=cÀyÖÎߣa3w5Õ÷Pëw†31¾Ë‘ÎM?¦+ñ~àgÊ8oJŒ<æÉ4™Öœ ßÜŸvæQól?q]]Ÿ¿Ìq„ïehf ‚‡*ó,Ä:«mٕܧP¥cú”¾dØ¢å?¶xw î`ý“¿ÒÞëþç»k‡E•4qwžH˜`tsò¶)Ú…,qyS¬~J!ß+ÑŽXÿ˜ÅF"ÊŽÆ G¹•*oÂ_ÜüÆâµƒôšì÷={—î>–‡2zRö‰ÿ¶‡¼£š2åõ'Ö+†Z¿¼Ï3Ž ›3Åi–°Vs‚w`”ú5Vô­ó‰ÛQCú³Š(ݬÕäñ1‡×\„1O¿wÞ#Ô ·wð7f÷«`²{>/v‡=Q=£kL’¦’§¼J ÙToöc’å¸ÑÎv Ua)‘ºk°½þp°gˆoZ®x !mo‘cÞ¶geÇuëZ<ÙDø5ANTã:¢tg÷TÈ7Ñó"Ça«‰ëÈv¤ ùv`ÁR“ÇÈf?Øù4„•ƒ#ËG$µÉn­…( ~o 9Ýd-PX.‘‚[L•ö¿»ÕoAÊM’ ¢ðX€56š½öŠRÊáaIR]­Ò)+t„èƒZ…$³‰ž'†§6þ¬EØ…à÷r¨i±”û´ë»t}òÚTÛA¢š¦¸Gƒ[ÞÙAkG8™Ý·•Ê| Øh§œVä—pÔ/ت ÝîòÒâséK!7”Úù_¡Þ¼g}Ô•½+®ƒc/~Ìñª¶Ì¡=gI/çÛ 4¾úÛ½(’’ ª/ç÷¬ötHt|ýhüY6+A]éû¹¹8ûÄõp žSc¸Oò’âäºûWdü¦î¾Âlד* D‚`õ¦VÕ©æ©ëßœ¬ƒ¿‘~ØžÞ!6â®Th çè£XZIRi×¥!Ùüu½5»[˜”®Ë?q޹!Î§ÒønnÀt$]".kÔÅxÌibwšã¢H$ÚSÓÖ­nýÄäŽÏ5èY5,B5¹ZjMÕKؤè/×Ã"ãÉݹ˜¼aŠÇÁ®[a*÷díÜ ÌN<ºwÔ~}­d1 ùæ[+l94¥¨ÌýÐ v”éÉ&žÁ@–¥ ZoI-—˜=•ûî¿$v˱ìQè¯x,A%tŠA}N¾ËF;1AnS @Ø\’êý›& ¹!oò[ðÀ+ ³JZÞËN!¸ äalkFØ6YipÆ3=鎺£"Æ š¯Œ™Tù²æ†—â%–“”›ü3Ì»{Ì9® ÉyÂÔ°Å›É%‹5Z¢S(a-ä˜4fÊ^Ôù_VjQ¦]Ôû$<•lçÕ{þ‚šÂ¢5ÂØ.ƒ ´°XÛ8Ó<ÊÆ^$7…¤·¸d×;ðñeÈ–Õ}þiBâ9¸<+¥û•}‚“YÉÄä“ ÃH;0.Íþ%¢+Rúsfð ûÜN$ØÝ+À!UñÒÑãIU{ìé¸ÎVL›Ÿi’ëbJ•e£4¨ÓÒüS`F&ãóhTè»Cí¯ÝDÑp…·ÿXŸêÜÈœ…¸Éè”~•K1ú”®¢ÒÅÉ`Ç€CððÑ<¸¡ñì¸P ìX‰a‹©ÉÜÂé%M!â¥çÅ^OÍn%*L¢8¡¡‚âTÐb}Ó/ÈÙ•~FÖ)Íñ¿Ð”‰òù}Ê“%;Ø;+k‰%c¨Êö²—*/³XÛ`°‹ö ÕÏãfû‚õD½Õ>9¦Z뾟ÓL fMÙnÐëGü¥nF)HYGk–Øëè8†unª{ Å"ìŸðe¡ ?¡ž%Ôˆ—烾 Z24ª5áË;ÐÅ4µ&Ý4å¼9K}Ôÿ9æ·Á#Ší›¹/RRók˜½àøÔz¥®’ ­3ýPÑnîk,^Îþi'!xq†|°‚!Ôx`œE¶žòZ²KQG ž¡í„ml¡û¾]‹¸)mçTøc%'EBƒÖÃÎWÕÝoa“ùú8-Æþ#1 HMt\µ|xÒA«âö9Íýú‘kÖs\Ðëc1Ù¦ºyüDp˜g¿qm¼YüœV_$ÔÛÒùü• ¸„$*«+0î&Ç™‡¢0åO¥fS¿€‡XÃùÉKË+æÄ'­à£!a¦èÙ~Â8¶˜|¯'¸×•õI3Lf¶'‘]0DÞ Ùr¬© dpMlxa^¯ÛØ‹—|W6ü ˆgˆ±{'°Ð~‡Æ¨Ø9Ük¹çøWPÞºkJu§ÜJÖÃí·Ú{B³‰ùb;š¡,×›Zs¶‹3¬…Eúe„ý{ÏjJÙŸ—Mð…äNàöè騄weØ«½Fb·¶O{°g*æÄ¹Ã# Z,¡†BÚK½e TB.ùÎ8'ÅÏA j¥´Ã“%µ&¾‰åØVöL‰55ŒU19WêQ#H@1¼âç6Ðj±ø™»Qí:;ÎD“`N“¥oÄЃúkn=ˆ™¯ågåà Šê-Q9*‰ñn¡N"Ãï)…I}6H’‚Ëí†Ëœ8® ÌòSœ\í…7bŠRzsoI˜üz:Q€@¢ÿ¯R˜—1HíFå66ˆN¡(²¥ â8äVLWEf'4m: ÀrLf;×AAŒfPTjVDiÀâ­È'2 äB_t[‚?ç@ÏV®¹m?j™Ûoœ+núƒîãü‚p6DÖêò㿃ãÀµÈî¸ ”w±tgs¤)Î[ýÇVÙÖƒä__€û5µ‚D¤Ó)0®·ˆÔ…ZÁÓ¨çfÛë.Æð4°_ú©7³^5CªÍ³cR ãqÿŒ–ЊԴò à5ûóHé±'cbw]œtgÎYëO±–Ÿ>=@ZÝAfµB‰ óŒoYÍ÷˜Å2š³ñ6‰¶kº2KGB®° ܽtZ‚Bg Oд8ލÉ´êA4î·Ûw¬:Lò Ïïm(ÃbA¾þá¢ÕlÍ,>®ŸA„ÕÐQJ’óR²\¸²Q²8.D¹žÞ½o-rª\,nxÖÑ•Š‹ÀÊCn›bˆÞcŠNݘ=ö¹ëX½ 4ŠÍt9{qoÎ q?lDd†ù©lÆ›‘ a‘@ †¦q/5\W# ‡PR—J´°º9¢+y×þæ Öø\;}9^5Üïꋃä[þH¼KkK±¼;½[ãÛ€N‰ca[««·)d ZMz)di&ÉlÛM>#³¸µ³àÊ — È Qï,*Àí‡= æ‹Ï 6®(• ”zõ !2 ¯äz23¸ºÈÅMOo{ìC 0©whQ²z3A^$‚ß©à„Õà9×;|¬¡vbÿ³¤Â&…¼ÍÔ†(¼4ÙŸÿÂÀe÷VPPüëªrT{ÔƒÎi¥Z ];}Â…Aº³0¨µÄ×ú(‹jË‚V¸Ê4ùràöp2ΙTéAõdˆ+º’óY™bOR‘Gõøzwb‚­Õä¸iö#ÙÌÎSÅ’,†¨¤ˆF—Ç‚m…xàIÃd€*j¡(âÙÕÕ¨²Æ0ÌSºc6ÓAòë”ó`(8mtmŽìŽä©äüõülŽ3ø=åaM#†Œ£!ñ426óx« uï\rÆûh{3‘çµuA—;§ß–©¥w ôòš]ñãVâp£ÝkNýî G‚¨d¸º±]ÔÚþ¤ºéŽ%Ç•?h :Y¡¿Ã…».êà§T¼vÎ8:Y6‹@í]úçŠ-Ý»ÈÏÖ²¬@`PgX®QÐ$0­¢œÿ3¯LûèøàÚ(FAÉ@–O›ƒùèTµ.øO(ðsÞŒ.þi´²-çíF£+š×qMh¬ÂûŽnwýÇFÕÕ®EKØ’äà €ÑÝÃõƒ{ö²"‰+ª _xÕ“„$#ÂN위¼Ñ(šL‘³e &wxƒ;“÷ioM1T‚r8@:f/¥S+ñþ•[±r–b-?ò¹•u9YzFÞ”ä x@Mª³ª4 ®dq‰l†W“–ýu"úBÍîÆ||‚mÍBÓhâH—×@€ „hîYI¼õ©í³0ì'S›ßýÈŽyÆ&#¥Kj…£ (ù³ÊÀü`J›ØS–¤¯ø_¹\?Ï^‘tµLJŬŒ›·©EûÐêÅo ט˜ÛϳíÇ>X|Âö×AxÛWÙÝ ’°8p Ê$¯„qˆc%eÔ-"µEbr7±lŸ8·›k@ÙFA\ƒIpb¨4É™™a$Î,vo³}íÍiq@½=®àÍ¡°`;ïZŠÕ~ ŠÙ†€\ȇ^Äz`Y6-á¨Þ@Q÷µ¯7îlïŒVûƒÈ4Ω2µ~˜W¡I` ÖMÐ)ÃÉŠïb  l%Ä£q‚a"ÄÖ”â£R…= 4d‹Jf0—cÚÀêÌüóâËI×Eÿnc›/Ó]äéy†§ZŠ28CêC©z\kþo®XÛ çE œà¤ÅőŔ¹~/…ÑYüBÝW{ŸÜAÄ#¨hÃéÂ|6™:ƒ½>Šìˤ=íìꇉ¥ÛQ'æ»5o·¡_i§ö¼õãÉ;8ò!Õ9t$§-À P´Å©‚Ô¥ÿ4ä¹f®ï{!ôè,'Øþ5Š7ñ¼¢õ¤)î•Á³Æ ë'­¹'×Q?àõ‡½ÖòIË4ƒÔ€ ø ; “ªå+Õ cg9ƒÿ9šÜšËû1’2pWÑãuïWŠÀ–+ÊZd"–ÅO]Û¿)m)³yvúª­bË6Ð}|_Á>.úG§§Tþl7‚Yçö£(N@§ãÒ„ÖÆdý´ úM¿Vy¡åjX÷VŸOÌî…îÇøóÎj–kŠDÝk"¨ fF§µ/ƒüù~‘8R´ÔŸuÝÃØ‘;䮦¬dà;ÿåmõ»å®ìê˜~ݰ”d€ð8ÒBÔ¨!O‰û@´›æ*¬íéÚM«ºÜ(‹( #hUÐ/[1€ÏªSnê36Œ¤Is)¥ÐIç"3ïlÄ/VHÆ8MS_ºD¿£±2W×°Ä*÷/,€±Ò[½7–«íbú²«©ÖFõ=æ"¸Þ3xNrT*xwCìh:ÚHXf¨Áø.Î'x̺=OÆÉðKæ+ÙõW? Î6A ÿ–×õ¶ôbXËUÍ£ø’î€ÕÌÑ ú'õ¥kÓ©ëêmš JšÁ &l¢ŽÁ3u­†¨/[+=ÎÖzôÚ ó(‘^bXÊ©ûmJ?¯5™i"ÐÑ€–>bBîÁögY¤bÛ• =³ºb¬ZRlöGhÅÈW%•v½–'§ÙÎg¯ a&Nµ4¯³ÿ¯{JÈf‰¨6ª¥ïöË¢tмu¼TSéG¿â<è%µnša=cé¿!EQÁüqƒ;iÙ‰÷ŽÝ×BììgÑ`«J‡õˆPEÕt%ºï\R“|«ã_ŒïK?–Ø× iȲ¼hhgF…±­{3e ÍÁz‘˜}†EÌU0ˆ=ôP=vA€¶Æ›.-¶ïtÁwË š½Ù¹f䀋ræ2…a"©—8¥W=èSUÈ¿¥È9ÙÊ-ˆjݸž(" 'a¨­ne4ÓË\øÎëédäIå·ÛÀÚ²5LÕ„–-J5„êÄÁ'‹mŸ•œ°µòrW.ÁCÛlÓ Is,:xó¨}Ž¥n¬q5uèâÔä‰Àå]HÂR^ÐfvÊLtC6X‘j¸²‡6yß¶ýÓ *ɽÿç‚@{áÏHo·à:ŠŸü ©‚ÎWÕ­!'ü­î•ú=ßaËah´ë0^8ôS\äûŸO7 [óãÎâC8)wЊa4Ò¡¯eµV!E[Ð5ñȘN™ÎÚÚŒsû}YçºMt,Ó[lô´Ë¶Ç»øÍ ½PŒW1m;Í ¼Ï„ŠCDn8­ÐkñâYY²ë?sÛj·©xETb½ž›ÉUÛœŸÜ]¥ÈüÆngÀp¢ Ma“åx°Ùô'¼Nø FÛÙróÂ#J̸ïEïÊä#0ñ1á˜Ì'ÛJäUÔ[­ÞKÛÐúw‚ žO×Ò~³KÈ9(w-׊V߈:’sç¾ukò•³ãrîmEGïtŠz˜ìœÕXNâÒo;xJ7XÖdŸÙI…- Yã®k‘ÕHXQê:âÎÉâÁ2aܪžßÛjagÇ ¡{RLåBïts†‡êœó¶>n˜ÿĶÒ“0i}Ëç/`¥ŽE±d1IØi{£{ûj /ªN|fp.£HËîºÍJ ÆþÜ€× äD ñ.,×°ì­†øºK¿Â¶1Åè%n!¶äW =±F)ªëâŽÚJ„6»`øþ T'T îŒ)©ËE«ÈYên¼ìEIù½=RH€<ü ¼bƒKX¨„-—ÝxëÒû::¡Ðæ†Z½DÎ Ô‡*±ÒR°Y¦6gªŽ~ŒYøªjV?‰S ×/+ò#ùtvÊP ZѲx”A¥^È1ÎÃè]ø ),ƒaü<œ¬Úæâ ‘!¶sTòúÌÎÒ„ÑŒ8˜=îý–'Wç|¿›8ja=áñM¦Ã“ä÷›î1t¿JK;]Fqa`™Ã  MîÔl˜²—¢ËNOê^î:J[F!» Ê Ø{H•TKÄõ‘):wå~S`èµøž0êÑZWêFÓù%õáxt´ÖixlrÇTMÝŽüíO¶B \q¶‹Â IOÖ‡.énE­üEý‚^aBêàû+CDš–‰Gp]Iç?ÿHÑÕX )[…œz‡±ÜL*ãH‡PNÙp#V*q]:ëY¦¯ø"9EÁ®R>Õ>º#øV TÃß²ÿÚ ß§Ô]¾æImi‡¬Š˜îS›J"Äaò!8HGÐÙ-R€NBÄm—œÛÆY¦µJžó?Ü€Y˜}qéxa†Ïìr}¯%ÁÑœP›è ²ä+ˆ&÷Éï9MÇE;øëxPËu·K^5¬<¸p#ê…î1úËrÆÿ堦⽒ŠT*ž  Öþý¢ÒÑD›ë|Ã7Wž‡Ói…YSHÕO½/“ç{k´&^U “+sÓZyHµ#äDåž~Ó­_7…»¾f‘C›°aDЀ5Ð4 UË^KBÓšª\)5(šÅUŠÙèBزâ1嘺˜r8x꥗¦W š;M>¯ž¬Bæ¹>ƒÀ-ìz½‰"øõ¾¶üö ïTö¥UR–»òÁ§QŠÅQAÔáÝG$÷veÝØYÙªÀV\ÐGOëÆôGƒ rÔz¾³–¬›&ô FûIÑJiŽ/d y¨ŽC£’D§ìCÿ‚gF?(KžÆ5•²þå^[BsGׂjÊ-ž,R2‰ûmÃ_ ÓÞÛ~5bßß EÖÞ{O¥!ÔŸZT:ΤIr$j7Yå§X_Éë¼·`SCjTgØJ±@Œ÷özJº4ú M©¨ÌŸÔhpÍñˆ]‰ñ®—…òrýÉ]ñŠýfùìoýÜqÔ¹½"ö¤$¶šä«r{h6€( at§„JYëÔV-Ýbm¢\U¥ΞRkT@¤«¥`¢jn«'ì‹.œ“]Á ¢ëÎÖ¯´œAÓ1hpH ê×”%YUƒœßí¶ €zCÕ¬M~û­¼RåP¡,†¹} ­¥XÎÍ%1Y§’LíÉLÙ(ïÖ-4 O»cðË07X>×1g®ßAµ… ë^«´„ƒv jšÊF{¶¿9ˆ{ãÙ3TÓ»‡¼ìÑâšsäÄBp6ÐÔù®Jgû¢¦¦Å*ÚZÞ*ܱ´µ,×|®#ô£)ì›ÄÁ~Å[eê}6]c"‚KÿfƒÒ ¶ˆ½HS®1ò‚3;¶Ô!±KU¦X“2nHà.±þ¨­âð Lé-s©¯9~L–¿Ðß’Ãf; ‚ÊMö©­”f î…×]YŽ­ùð¾ ~«å¥“^ïtHž/ÖíÈGX 'q/?ƒúÍG† ¦º1Z8hþÑ%.Œbï"­.g¢°8yé¡¿/“²µ«CÍp2¹€Ä¶ˆœü£¦¨oú82,T@Ó¦"h">ã$Âã¿|~a3Èy¡Ì²tÀy…Ð0r°\#È*íI|„!«Cd6‡´Õ¸ðG«ýiÙq<ÚÁ]´,, ¢ÞQÜ-üu<þêyêYI¯c8> ñ·¶!Šóö]dc'2è°¦ÍÛ·]B©ÓRûØ:‚ç 3´_6ž# UÛ=›Dýåæí-‚qo<éàÒ²®ìïlÿË%•õÐQêxž·Ï}.XÝÂV 4…m^‰ÛCqFIÖ(èöZ¼$>–v¸à)mŽ+HŽf7EN3®FͰèê}ø°²Å©qI‡²C®)ðHÀ¸ãóè÷‘­9(„¬ëÿsž\°úQ´€‘¶Hó©õRˆ;„ ¸š∨ÎaJåö¾V§üQ‘Ñ|c£G»àŸ´WZ±y¬¶iã×Ë.â{Nœ)‡Z„îd¿¹5u»WXûwkG³LÝæ°DM3…Ž.œ…4žG* ?$퓳©á—Jt“bãžô™u[ضOfIÉ£Žäï“4¿~ìù­MBl:ÎÁãžšl’7Èï´èjÓ‚S»&Ù@v ïM£³Áp“²ïw-;KaguÛæ¨.AåÆˆeÐVÚ+Á)äFÚÆb* ™ w$›!Î-«¬H°‹5Ëÿ»ÁvŸ¬aKÞá)þ¡›l0é€Y––³}=½ŠoJè ë3}Ÿp,–™ÿ¬p²ãKL‡înŠÉ”.¸@+9»*­*`‹” þ\ŠpþÅ'rË ü×™ºŠÞÚo«B×L†Šï ñJûi \úŠ­s‡Ô“úx—W¨­¢ô*Ó³,—×`´3 Ú&Êã—}nßwê_ˆ­R œÄÃQofîýªK©M•¿iE~kF|{H“2û¬2¹)‚ ÷S<“<’öQóÈ<ÄœO3lQÛËÙmÔ( ø‚u­°\Èÿ½ kF½µéŠnšù 6û?Ñ£@lcX¬ºÞ<#—ŒüÀÜà/fBËã°Q×mË]8HAë:ð¿o[|Š~Bv!Ð2$´ïÜÉ7‘Ý4¶ªû„ä ! ¬¥`©\œÌ‰}±Ñûšf£(;Wì+Aä|Å¥Ø:öfE$¦žßåw.ôé·?V;HFJŠi<³´þ;Z ÙÆå¢nÄÜcÅq€gAÝ·&RÁ6kp K·yH¸ÝλöŒqS9¡@³‹VÍ)ûe3#­‰©Û¸ ¡Že¾Ök€¤±»Ög´xš¨2e„Ò’R|ÈjÈ»HþÓœUA_sL¤Ó{zÞ*^ÕÏ©‰Ð{Ížõnb‚2'×þ~äwœt² ÀIbã8»çÞh>ˆ—†³‚é×:;–Ú}ùlþ{³{ˆõuœôã_ˆü ÜÇa+óes×m¶Pêt52…¶5ÌTÓtM4æ 7¾Ižhæ~aè¥0nûñïà+þŒ Ÿ7‡’©±VtS线–†s çœDéªa.מ Ù G6k¾ý§ÜVe¤… Õê½s¹vJ?À £à¼$ Ùg‰£Å¸‘K÷UÜ^fÖÀž³‰øƒŸíÒôžÆWٖ߫´×AÒ–†Ç4¶±µâÒËWhD/c²‡*1кš{1(w„™¼¢ëÏåiÄM·qÙb§Jðņ4N@±eɾüÆéáSVÝEÛV¿ksOÓæ]RL9¢b„©|_À§™Iz¶~Üœ;”x~ü«ŽžÙÂô­Æ«ý½SÍ` 2 ǾBèú^yÛ(±í¹}›F­¨ü¿­ÄržZÛkñhÈëµÄåtÛ#äDõ•P¡X°„7€IÖÁ vJ]&ÐCS¾µªÇüÇí‘’~)™7‚t;\Þî5ý𪰫‹·¼N5X`Á–öF2襴ǂÐÿô°CrSoìj&°ºwB™þäáSyÞë ¯Z½úv}:ÉØaȼ< Ÿ.ˆ\Œ(ìßø5ú÷5õVásu*—P#+hLC¶€ZsËŽyâ+Žž]—,û‹â¼86õù²øó9<->¡j›©ÞXP'ãi _?Åë7²Ù³œ0ëw·dß#RlÚ6£æ+S ¯?z{m_ãaµ¡ì6Û¶ x+±Gç§­·uýlõ^¥¶÷Ñš².¤™ñÖ3éB¡ ot€tL†9E¾ÔJ½¹þ)Ø+ò¡°ö­XH¸ã’Û $òÓÉM¸»Hvă–·íè´À›u8ùtøÉ[Ÿ]É÷ν¯×‚TˆT”Ñ‚DÐu«Í?Þtκ ô£È‚j(Ë¢‰_ÅK¥ª¥˜ãÞ®{Ú{sUËnÑ‚ÔLÝôgŽUŸÄš;=ÌRg€wì)é¼~VRA¾™ @¬“¡í!I þ…Sý'ÇååõïûÝü‘0Ýv7­àQ%æîó@Å1¶nyçÿÌ…ži†Ôÿ7÷ŽœÁpC•[ÆmjCPÛcüã7äç´À‹ÞVÙíóÀ~åĆBÇ•Œì¿ÊK·éÑ =[9I-ŒêRÎÛůۄʒ¯VÏ|‚k#ÆÞÞ0>½6ós5胳† ë¼Ô—-Ÿ}ùßß³NdŽJÓ nQdï§ò© ,û!´”7Šqe“Ø€´kñ£ï××Z†ûÑŸÐé‰vƒPEš€ïÐÙC²…š‚­ù§ô¿)™d3cf4LÜ+%ž 2˜ØÎ^ŒZ¶>Ú˜¬‰aˆd$I•/¡ÖåÎ?Úß›säfÉ–Ú[ÿàØ$fH»(êLÄöeÒFµˆõyi?¾›¯×Ô5ç¼t—Àˆ®? w$Ä 8ƒ®XGl.øbâàVßIÃîUc ë»Êõ¸lAgÊ„2Þê_Ñãö°s!¨ÖEÊ#_×u›Àá[~Цê£óÙjÔéc“¡Ð¦@öÛ[å9pª©±CÈ­þn›P¡¨Ãì Þ¥fnFMªgþRªwâÁH(˜"dˆÉÁEùÊ:Ь2¶³7o‘W鹈rï–ÃmA“€z­ÇƒÛO¾'ñ³”¡uÃÂýoÂlà’ã4©ß#ÙëWàÿTÞ Mi¯˜ÖG½ÛN„¦†—Ð"–žÉ ̱鶳×M; Cwq6ôŠw0Mç˵ Âh¯‰ÄÅ 'VÈq!ωÁñc꟨­²æ9¹°áVs/iüAëÃU/¶a ¥qmµwƒ=IaÜàltkV¿wCËÈ}3Y@µ*±4cÉ6¯rzK„Ôü ×`3ª­6³=¬ÀÔ>­ÙlqÊ/ð·6Ðò­¹Å;cø/&f¬2ª¼áí'ë§´dù‘°]ÐÀï/¦*EÍÈ”¿€7Û4ãSz“&ÝøÜ¨'Éâ–¼Z!©"5ŸÚ&bóµðò:¿ÇRö"§YÙ3I_¢¼lÄfw]ÙwnÅüâ¬{C•u#…|,ÖRŠôÅ—¨~fýAFâîñËrkêGd‡»º÷ð¤œBàjkƒIí9õAMÊSøoZGêrÖ ½R-q„ÉhfíÍ„FQÆÎ1cC™´^0)õsfÁÞyVPà^3x>©gc&7qçeÏ o²U=d²mM»w¶d` ÙÄêKòR¨­FºÔ–‚賊¬àföhÚÓœÂЄÊãFlh£´<êÆ°ç;g¢3'dMbñ®Y&HlÒ‰ïŠ&´^çR’ÒÃ~2sççWw<âK¡ÿ„¤.êîÉlƒãæ ê#XíoÍi³`Æ,›-™¿t¸âž‰Nì9ˆ‹˜ÞósáªUb®víí1`Õû¹Ò ìsXâ¯PÓ‘•ÇÒì¯mþCç±ý#ÓWQNãŠò îòE*cÁÈ,yõ]hg_¶%2§£‰—F›‰7!ÅÇGO$ØÎŸ…½Q‡›!jùÄ4œ– :Y‰\>­<À ?×ø÷¾©ïyw‰ó ”i­´ ù¡Û|ßÛo=Ÿ$Þ £Î'ÉV`w·U:õ ×E ÃVaá5«lÿÝ…Nñîö@)”¢ ‹êËI¥ÅvøaºmÓÝÐ4 ]Ö÷ caŸ–DÒÈsÇǸ7IÕðù¾‰ë†«ÈÝÄâÖpí kÿ¨v}’ìÙ¦•ªX0þc ߸Ìä¯>²G'Tê³`(Dìñälºƒ-±ó 91PÞÇ&1#Øâ/^=ß²Öq`‘s!ºÛg®D¼ñ2[› ¤}öÂp\+€ðøÒ²ãt¹ôpíýkŽïwJÙ\ðº¦_ì tÓñÿN>ÅÛ§IM¯Ëý}Øzqòzü"cgãG^UÅ£ÜÁ×߆qáAxpÔ©åžÉÊ1‹þirŒZ ìFPýÝŠ³q»¡ÙΈô …è0Έµp’¾7 Û¹æ7Ìý|–ñ&=ëÓ#Õ×°¥±…žJr¤·÷M¾wl›æIQF¡‡2`Épðcbþ}” >Ž]¡x|ëuáR8D ËISŸ¡|ñ?@ÝXdYmÐÿ VÀwÄý.4¥Þ8²ÝËÌ?¢ w¥¡@ƒ–Öo—à&•YgýºVܼ›íj:Zç'FgKÔm€cø¾+ÖeÌbßDêáϱb4SÞ—æ†Òáå]5>–—€OKMË#*õ¼æÌ©Qý9­ÿkÄ€¬ mCtãÿ"9PMì>¨ ¼à8DÉOð™Žàto+Y>×lýº§PørѸ6oÛ%u`½l"xFx ƒº:S çnôü(Õ¯ËëÀxtÚø¼²ž¹fŠ\妚Eí3]¦Ç§–Eµ2—÷ëyŠŽúé« „~øX3÷GC­z4rGGuÖL”D f×QÈOoSiÂ÷·™NX¬ç¹|•ÓÛó@#M_¡1*Sã€cÄK2†c]5,Âvß&rR9tæ…b¡TDYÿ°}“W Yðõ×1DtÀ’J$iÛ éØÓµm¼ìÑK=oØdwL˜û% "U×Nº BZ#/¯4ר•¼ŽÆ}h‚’À ëqµ+|(µÌàu»œ–7MÁ¢i8 ödÍÌÐwÇξ·ˆœÞ.ê8 ¢/ˆ,{ƒ; jÊ›‘]3Í’D¸Øó2\•ßßÊÚ¨zhÖ„ÁÖ½$ÛéåèX…挢ºø‘ûB¥Œ nËÙ#pL%Âïõlm„Ôò¿T¡û•åÚÙè#‡Ÿ`°-Í÷ÆQoØ£ô}½Eîßß•b÷þÍ–òð92/4) ït¿c\œ„•,d¬º®ó0,Ðî†Þ2¬µµ±¹ˆIó°Ÿ£à‹8 m¿á%µ¹´½‰çY~0DgsXkÜÔŸ{ô£Ëž-¡]KÑE15#(}•®VîvV^Q—³œ± æOÅv•ái*ˆ×@'Äu‰âW°ðωL;¸|7ºh“üiÈf÷sÜñŠ?4b8꜡áéRG¼SÙð‘‚Ý|¦” ROf£[ãM°‡Y¨¹3ÿjrI‰ÎÑ>îÔ¢«˜†Ð‰ gêÂÓPZàdûS.¬Ç÷š!›¯cõ„¥ó2âÜ´/pQH]ÞÓoߤô6<÷Dæ:ZìV|LÛt|U¿äÕ*ŸÀÕ ÐÒ3ƒö–i/ð¢êYàö êÊ™XBõ„àµp©z^¢?CýCaZŒuÆ—†Î(ÂîE¯Û,ëRðAÁaæŽï‡˜™Ët8‘ ävôA{0®j¶ÑC‹RÛ£Ÿ³Õ(ëòÅV)¶š¸Q0¡¡…«L¢ªé.)öË^æï’æ¥öt…BÌR|<«B};YŒÙ/å‘Ï-oŽ×®Å}‘&>ã4¯„@¿,àQéí0²žEtÜT£Fš1(}½(P&B†8 ì<Î\[ÅÇÁ–ÉÖ} ‚•ÀͦÍê£O¢¬êÅ}p‰ÀPç- ÃøËû$ÀÎ-Ãã^å1Ý^J:ʲ.‚Èƒë® §4³­^ТNÜQT ÇôŒɹ~ê ¶BKÝž#åV(¿\š2Ga·ü=§+ÎO^ÓCð’‹Ùœ¦ZG.Ug&™?ô\ È9å³ðr,Ï7ršø×´ Ž»“Í×–rþ ±¢ÍV…O‚×E­ÂêV€¤L‹ÈÞÿ©£Uµ§P3-Š–Ù‘î«uƒÌt#æ÷A¯áë¾9ƒ@SŪÅùe"À½©J*°î™–Ø6Î~ÓœpDfâQ¸}p:4Z›tŒVmXMêyšõn·#;²CkOHÜt!$€Á¦ÔGÃ鱚Pu‰Î^óý ± š- 6‘u‰˜"o™4yñ)ê%ôµÆ\ï4ï #£`£ï2òn‘|Ζn¼óðú_LG—á[Îô­t…ÝìÑ‘ôø içOR•#BÒˆ¢üHé”JŠ¢‡ÌϤ•h¦·(á{ªxvƒéõ¨D¹ô¸Ÿ¶GY¢î$ÆÉ‹xÇ]mò §_6Óæˆê@µèÒLjÂdA&]&¾®Ö&Û&PðîžnK÷²zå=µ?#Ü…“$Á‹°ÓÎÁºõèy<1æÂøSÓj—ÐWZ'ûCÛ,•¥ìå¡K³•™’ÈþÓ‹V'©ÚÊ[Òo¥0Þ…~_ÂRŸÕZ´Ðᘠ:‘wöïVIW/Ó|×v-/¹”õÃr¡zÛÆòf'¸Esð™(á½Ù3L|ÑÕ­{“£ú²Ã/µ¿®ÝýÖ—dÓé:Å{«áËFµNd1k{ù¼‘Œ¾ßH§õ,±ÅßäÄþoýô¨((ÁåÑÛ"{§™1Õó{óÙP –\¿ñ~›j/sËüôQ€_ úE„:Ý™VÈ]ÆwE)Ä8õöÛÌ pa˜N³\Ó6’;{{ÚôL÷ÄÎsMç¥Ê{œ3Ó—ê1Ljô³™8‡¯MgúoÏÔ}n½k^€ýo»-nÓú£bnÏh¤òÚ\ß=Yÿ¸ @2­’Û 2r×fêÂChJ.Æ)8ÜÀËWöBòÑ“Ð1ªöÝ/vU.FèQ@Ö¿‹ÐEˆdC,÷Õí¸¸ÏŠOñåÓpóá›9—Oa+g£S¡€Zã4]“Br”[EÐk¨ºèZfÌÚoAlÑKûX°Ÿ’&‰œÚ u8îÉtô‘þ^–°EGÖ¹{ÿ§Á%WQ¡lÐÈ€ôUküÌZfųP²™Æ »Ç!Á‹ëÝ×YÛ]o?2™.Z¶EU›­e> e(j/Ξ MYdJN'WBuªç©Ôam7“²ÖÐLo#8‚"Þ<`a+„± Ùò÷üQ$×$tÅ>Û‰‹Œû%JSãM×im‰5!+ž„¼„ÑŒžXé»cñfÍkÅù©pÂÆC×›ÙA:þhO¨ÎiMj¾zÌΆ!Ö…i’œá.¿©è¿Î‡d§ßp×ì*ÌÁJÞ)èv†RÎJÑÐ3¿¬CŒó¦×÷ÔËø#ä–‹Dé™Q'Ÿ%Äf"óÁk¡ìŠ·|?"=0+1i†ƒ“BäZÙ’h_ÞÿËš‰kIŸr3Ûð4øw:ÓãQ)J©rxˆðæl÷(¸þn: Íxœ…ÀͲ¡ªiK ÛS3')¸ËÊ :W-£›q©Ä‘¡ÆÙƵw£À|}FÌÑ£3>}·õõ ¨2ðí Q×Éš?À:í§xö†É£3él‹J*aŒt# íB?Au·Ù¹H®Bómá{(^ÈUD ðÊf°Tñ ÅVõï¿r±[Ÿß¨bvÇÖöKÖ䮩 b;X¢Š ùuicÆŸ®œÖ™À¼›'pº1,ȘOõ¥ ã”Z‘’?yj·”Å«AèÎ6ff\ä`Ÿ!->úº uÿ7ñàû»”𬄇Úe¬e!lêåc\›‡™ö’@êqgCb)3e®¬øÝÄu3œ£¼ˆiò&w®È;­A0ÅÅ›•Í à¼¡OÄwsw÷Tx`ü·Ž çÅ+á©ÂrqœnTcÍØˆ|A JtòëwÞ¹‡$é<«¶\1ÿzÀ£.®ç±ÙÔ =‡Rïå©E¼ïE|³Óy!F·Z{µ<ù¨kyŠ¢¡{÷ vécÃþ`ˆ€ý­ƒtv ›ËçZ‡½Ýf7ÑøGxï^›9Óõˆ„(>h±Ñòåê h =>_hã=¶" 6ð²·/n÷°Öh7ãÅÆÃ0íï¿¿âj+—;<ʽáÉñ±øV ðGúiL2D †ÙÁ½ºA¶§2¿HØMoOæJ˜¦ý³W¬æŽÙ…‡äÝX1)ﳫcùIDSL„Œ5¾•ïH\œwÜX'í*ûß7èªü–!ÁØ!åËÖ`ÙW‡fÄH¹ìŽELC*£’B=`;‚lÓ?x¶ž6²‚ÑKâ 1Ø5~˜ç¥ÓÐÍèÛ„åQÙdðmI»X‹Ä˜Ê—a•¨³OÛÃòñªÑóa9×îçPIlI)E8ÊU P‰Jû—'vÀGºcyÃkƒü¸¤£‘Ò0+,DÐ2d~Ñ¿0Tš¸ ®?ˆ@ ãgÏqA-¢æ¦5ôy–ÔøD¤Õ·ÞÁ€(¤Ä€”_ U½lžh//Rg•ÌÉÈŠ£#vy »XÜF#“Öüc…”žgzRðǤåe:ÙQ/ÿx¸;!’ÀlÍ÷ÒÍ Ô'žN‘ú#ÅEón?™(û“Ò£M`ïÚ=$°{ë\N‹ô˜Xj¾pXøÇÀÑ$*VÌ»ó(˜qV™‹ŸEP.ej -·vTñ¶ LàVt”¹Lõ¯0)&ë{%„Š  —X|çÔÞ1)2MwɦáÞ4¸•¾=|>9¬ûfÒYñ9Ýzˆ•Û9ùú9,´yIGz¹ñ^è6¸u+àêú–ë+Á ä0òž›UŒ[ .ÔR2ND{ _¢7òú—sûQí ¡å4Nr2mi~BÕútEfÁ 1éíG$ˆO Ü6ý×>¢•Ÿy]2ó8"ñµDðÄÞ‚=iÉ9&h+Ã3 ¨r˜‹bJ º«£ …cGÀAû1HíѬŽat¼†±‡¾>þ0´ÁæLLF¢™lï}ÕÂŽW‘6Ö|û*)ÆÇ.™Ë7¢ÖªA´4AÞ9Q@¢Uó(º±Ö$ÆA2AJ,ñ?,kŸ…n‰Jð®ÌLByjï]K-ír]­K¿ýS~õqbæÁ 6X˜Í[f|50™ šy”üb¾Eë:rŠoVN»r3 uÙiT®AÓ¡¡³«”ô•Ð{øÚÊ‚'É5^5G"(A$T"®—uqW•èH§Ô¥‘å©ì˜*l%?ÂrÛœhtȧ„èNãŒòÒ&Ѹ¤A7îFÞ†f×]¾Ûï’›½Œª!ß[׫ βieÇ ü¬°[ѱ'^ɳ·7¯1¦²÷.C¡”™· z„ª+•8¨ýgížý"È*XsÂÓ¾†©××ÃP2äo­•¶áM\©«f&þ»‡óH“ÃÌ âW<ÑyMS¦XðW/ØÊ®¹Å¬dÛeÏÛµ7]*4Ñ*­íiç«Éb0Øa+]ø‡MÒî£[LyŒ®›t`%„+Y[¯–weömR7&jxÅlÚõ’,þö²vN0=+vºúœÜ¾K7ì6¶Ê•2ÃÊ=£”êß.iR[<îÏ”÷œ ·â¼¬öÄfRhS‰Ù:•9/îàö•²èqâÅdzÿÄ$¦¶{Ú\{|@|.|*ßGiW#ÀŽÐû.c™¢\³D+ŸFeˆ,©ÓçLÊToZÝ ÿÒó¿¯&;{Œ×Å7£ZfeÊq÷÷Ë)zS!F£ÁkàãÓùnä¸m©¥W ²ßTº:¿f¤g¶¾À6:%Ì&³Ù ¢æª•'\…ÿ–> f “*³Š=¦¬¢ÄéÇ=¥xñÃqd»dDõæ“[Ÿ2±1AÌPË4‡ä‰„¸e^ÌÙˆCµû‰ˆFqîw½«ˆ®ÉÙ°¹)y’R´Ãfðÿ÷o‘цiP¢W ×nŽ=„iê‰2Dš°Ïø¿Šð½ƒ‹(”Jöqý8ô—|t äêÑPÃF¤{"Z‰ºz0¨î€Æ½}-ÒøÈkÝ÷i KK¬0€¼}ÓøºI¯Ž)gß³ñ%°§R¦g£E»®)¿ˆô?6i)¬˜$9)mÇŽîQÛ>ŠVæ ë/Â…ð=ЇÃI¦¤¡ò¦¯PBtö ‹ÔØ'LjrQ8·s:ö´‰¬¨žûYžC1q¶ƒE‰Ü“\Ó¬,µŸ=1ëˆÐƒHCtø¯wa$Þ ¾EòäLq L ªö|ZZ×tCDE­DÒ9é©»ÿ ½«áåQÏÝ:P3¨]´±YWc©nRg¹tE‹˜|†gA©:*_XÝN©$ï{=þÈV…îØiƒr"dÑ0œU†ž=’ß+NúC  ¾ãJK0ì½vŸ¨ë^ý#¸¾Ä5eK\wב™ãz> óÎ!zë­ú.ì«°±ú·O:€)?,-¸Š]î xÉŸ§=¡”R­ÁjâõÝB:a æùDO&@Muâ\ÛRV[L''ÛÒýK+_í \ÏÐ奊íË@è6r™®B—昌Ӵ°.‚(³‚ÜIøšl‡A¥:öóϳË!dPûaEå›^,p´c)”y™¨÷å©®s¬æ(A®3Éñ)ç^ Á¥“S XÁt¶¼5Ý ‡o£Œ†ÄÚV`È‘²¦f^KÊÄ5&l¾qänvG´ò,CÊþÛ'Œr.XÅæ4‹¢‘¨ê‚"øê05pò'Çò÷§¶`Xêž´ëjqð^jWàÛ`¼9qQc¼>™¸²O§žû©MàDWij¼k¥Ê„Jj5Ÿê *¢‹ 3”x§ß èɾ¤, CGÈ2û9Íî>ëMQQ·ÕÅéà‹ozñÖJŽFŒŠX¾ÀNµ3²þú´hÆUþvGG¿¢DœñŽKe<­å©Еa!“a9´F3¼±jæ|mÂËêy€¸`Qˆ„«]~ ÷µÆr©0‘î&pÅ/gøsB<-¡^&Ù"á¢úŒòƒÈµÙâ »¿ @lq!ckòv`íN N¢º"» uoÆ_²w$ûν8²ȣEÊi›å¶øª4w–µ†Ÿ a¸ HØF®t“*záÓã&‡XB‹öt¥_Ìê–*³ÜÂfõMHÔŠ¯ùꉛÊóKçc\d×z6]3&nÖFgቄ35dThSy-êÐMŠÛ˜±ÂßÓ@±%AïPocxáe*ÙR7­Õð+ÌÝCl¸\6^ŠØeq[úóŠ~Íïýç¯òï0F8жËi±G|lï›öGº&8¢¢j"ª›'É]ÌŠÍ.$<¶(îË©6¹Ä“ íÃv=¢÷ÀŸNcm¹+ó\tõegÚ!bmÉ×ÒÀÈ*ÊZ ™A˜ú¥Ûõ18 œÔ˜Q}'Ö¯ ä=ÑT{ÊäTpÅŒ§zœQÑú^¶o†E«€Ó.-)®hË/ö!ƒ5‰û¬óQ&nÈPýõà¸Äöº¸§ýþf® $Iãu¨ö¦+¬ÎËø@‘uìI àSã»4{þ¼êÑÙ'8âÔðÀ}Ëc{&c:ãÜ'¢a@XÖùA… ‰yÃ8¢Ü•¯.ÎêV#ÛÓ‡dÁÈy\(ªVya…$4Ü7ýl¾ïÑ Çâæût“J?ÈiOØÅõè<9`ŃæL¦ïTüÃèÛl²Ï3e_²IøWö£ÇÄLÀB&I‰‹d®#ÜHGÿNŒë;I¤¨–$ª§ .$Háà À÷,GÎ#À,=&íBÖö ÉP÷VºVq @éš@® šMªÕ¾d©v¹ÿâ&1#;)AÐFNá³sd?èhؾ:°Œú-»B½(攢²tÄrhH祣ç“ÃY&ÍåŒËi“n” ©[P§ ¡è('f´Í[&W bÙ>Gp'¸‘·»)r†‚#èàÍcwPMÀBдftngW’ÄsŽSeÍv[>¼Ž—dÑ„4‚>Ñ *ˆs?İy™§û­ð@øí»hh$yȾR"¦.³ÔÂÚË6($“uë}aÑx¿ƒœ‰¿)­C|ûkyIì¹½^ô½?]âJ(&É?ü±H²šLáÿësfÙmS퇑Ç'[ô3¸X‚ý½¢ÐLëÕ2«½e6¿ÖtwÆ¿†5M·õƒ–Ÿ9½ÙI‰ùqdW9*A¿¼ŽüýÝç0_ç—§is¹ÂhµhI>³H­ýéڮJõ aRÇ%$ºoÅ—0G(a°iÍP¨}Þ½a]sšk¾¾ò_ÒÁ Þ·)ir™ý×p§ŒEÀÖéÎ5›üܶ ‘ºÚ,ݹ­ýýϯ&l¥"]p•¦ ›D–ï ¿­pµOV[b xæÍ²‡CO‹>úë…öQ”Åq)> ±EÆÔF‡×¶íôY^2y#O›v¶¾òÒÕli|Í@-Çó,–{Яô0+®×¹´’„”ŽŽÎ ÆGºU¹ÐÄ÷Jvv:a(Ë-f»·ø!®ô ùûÀáQ «3­öžQ3 ÌwLããÎvF4†Ö[mupŒ6“OL¹@ÉÛÚ+²>ƒ½`uzOê)3ÕYP-ªOsκg_*_ÿM¯Éï´Úi@ÅsÄ®³ˆWèŽÐI¢ÿl£'dr’maÑWƒç¼6#O%EHè_×µý¨ŒۘǷ>t"õGÀ£ŽQYZ|d¿">ê‰ÌµêªÆ2Öe¬éú˜·ûý|±`ìO¤À ”Ó.ìP$וқÀoίØä_oSÎŒ-ðÆvÒ£LíCÇ]‚Zç’ÿPÑKÇa½Š9O;.¤,ó.NáÖüx”¾Ap¸(Ý_ö5`Ÿ¤#aègCðPº°ÃzÜ@­]¶ yÈGÉ÷q]¯ñ[ˆºù²¢µÇ‡ÎÉ㇭¦¥-,,~”êùc™ŠDºH‡.oíøÆúÇ~¡ÐÂ$ÅEU“ µa¬í-|vÄ{]+9ˆÚíê  ×zÕÙ`9Îj«\ˆ¨ß ¢©þ™yp³Iâ[ᾘ¡U¬0Ê2Á¤\¡{û&y'Ý‘h ððÁáhzÖ€«H­ùfÕûãZVáATÓ6WµLÚÿ²hÅä„èy´X¹çøÅ#SDG+—ƒÂßÛ4”™{ê™ïoÀ¹~oÀB΋µëO9Õj êö|ñvÖ­Âæ93w¢×$»—^Fîfm¹  ·šÓȺ§:ÆÿzQl·´…I)VãG'¸´nšÞd):Á|I¤ã5&Èܘ8êÎù)¤¸•ëÆ”ÅÌÚžÈOZ20l0*EÐ…3lÞ=-é¹û‰ºçzÚ’ T-ΞLv^2+ŠyÙÆ‰·†G9áàÅlSÆÈäÏ-Œ¸™!^©ç§#[ç‡|5Hf¹þaÖÔ9ñS%3ï ñ†De‡•Bpg\<€Hç $²–ùp-¹l䨚@þg§jR*c,&å;ƒÒÌKÞÏÁæŒjR*c,&å;ƒÒÌKÞÏÁæŒÝA¶æ´]|’&ؾ2…Ç6FsFeetransmission-rpc-7.0.3/tests/test_client.py000066400000000000000000000156441451315226700211050ustar00rootroot00000000000000import time import base64 import pathlib from unittest import mock from urllib.parse import urljoin import yarl import pytest from typing_extensions import Literal from transmission_rpc.error import TransmissionAuthError from transmission_rpc.types import File from transmission_rpc.utils import _try_read_torrent from transmission_rpc.client import Client, ensure_location_str @pytest.mark.parametrize( ("protocol", "username", "password", "host", "port", "path"), [ ( "https", "a+2da/s a?s=d$", "a@as +@45/:&*^", "127.0.0.1", 2333, "/transmission/", ), ( "http", "/", None, "127.0.0.1", 2333, "/transmission/", ), ], ) def test_client_parse_url(protocol: Literal["http", "https"], username, password, host, port, path): with mock.patch("transmission_rpc.client.Client._request"), mock.patch( "transmission_rpc.client.Client.get_session" ): client = Client( protocol=protocol, username=username, password=password, host=host, port=port, path=path, ) u = str( yarl.URL.build( scheme=protocol, user=username, password=password, host=host, port=port, path=urljoin(path, "rpc"), ) ) assert client.url == u def hash_to_magnet(h): return f"magnet:?xt=urn:btih:{h}" torrent_hash = "e84213a794f3ccd890382a54a64ca68b7e925433" magnet_url = f"magnet:?xt=urn:btih:{torrent_hash}" torrent_hash2 = "9fc20b9e98ea98b4a35e6223041a5ef94ea27809" torrent_url = "https://github.com/trim21/transmission-rpc/raw/v4.1.0/tests/fixtures/iso.torrent" def test_client_add_kwargs(): m = mock.Mock(return_value={"hello": "workd"}) with mock.patch("transmission_rpc.client.Client._request", m): with mock.patch("transmission_rpc.client.Client.get_session"): c = Client() c.add_torrent( torrent_url, download_dir="dd", files_unwanted=[1, 2], files_wanted=[3, 4], paused=False, peer_limit=5, priority_high=[6], priority_low=[7], priority_normal=[8], cookies="coo", bandwidthPriority=4, ) m.assert_called_with( "torrent-add", { "filename": torrent_url, "download-dir": "dd", "files-unwanted": [1, 2], "files-wanted": [3, 4], "paused": False, "peer-limit": 5, "priority-high": [6], "priority-low": [7], "priority-normal": [8], "cookies": "coo", "bandwidthPriority": 4, }, timeout=None, ) def test_client_add_url(): assert _try_read_torrent(torrent_url) is None, "handle http URL with daemon" def test_client_add_magnet(): assert _try_read_torrent(magnet_url) is None, "handle magnet URL with daemon" def test_client_add_pathlib_path(): p = pathlib.Path("tests/fixtures/iso.torrent") b64 = base64.b64encode(p.read_bytes()).decode() assert _try_read_torrent(p) == b64, "should skip handle base64 content" def test_client_add_read_file_in_base64(): with open("tests/fixtures/iso.torrent", "rb") as f: content = f.read() f.seek(0) data = _try_read_torrent(f) assert base64.b64encode(content).decode() == data, "should base64 encode torrent file" def test_client_add_torrent_bytes(): with open("tests/fixtures/iso.torrent", "rb") as f: content = f.read() data = _try_read_torrent(content) assert base64.b64encode(content).decode() == data, "should base64 bytes" def test_real_add_magnet(tr_client: Client): tr_client.add_torrent(magnet_url) assert len(tr_client.get_torrents()) == 1, "transmission should has at least 1 task" def test_real_add_torrent_fd(tr_client: Client): with open("tests/fixtures/iso.torrent", "rb") as f: tr_client.add_torrent(f) assert len(tr_client.get_torrents()) == 1, "transmission should has at least 1 task" def test_real_add_torrent_http(tr_client: Client): tr_client.add_torrent(torrent_url) assert len(tr_client.get_torrents()) == 1, "transmission should has at least 1 task" def test_real_stop(tr_client: Client, fake_hash_factory): info_hash = fake_hash_factory() url = hash_to_magnet(info_hash) tr_client.add_torrent(url) tr_client.stop_torrent(info_hash) assert len(tr_client.get_torrents()) == 1, "transmission should has only 1 task" ret = False for _ in range(50): time.sleep(0.2) if tr_client.get_torrents()[0].status == "stopped": ret = True break assert ret, "torrent should be stopped" def test_real_torrent_start_all(tr_client: Client, fake_hash_factory): tr_client.add_torrent(torrent_url, paused=True, timeout=10) for torrent in tr_client.get_torrents(): assert torrent.stopped or torrent.checking, "all torrent should be stopped" tr_client.start_all() for torrent in tr_client.get_torrents(): assert torrent.downloading or torrent.checking, "all torrent should be downloading" def test_real_session_get(tr_client: Client): tr_client.get_session() def test_real_free_space(tr_client: Client): session = tr_client.get_session() tr_client.free_space(session.download_dir) def test_real_session_stats(tr_client: Client): tr_client.session_stats() def test_wrong_logger(): with pytest.raises(TypeError): Client(logger="something") def test_real_torrent_attr_type(tr_client: Client): with open("tests/fixtures/iso.torrent", "rb") as f: tr_client.add_torrent(f) for torrent in tr_client.get_torrents(): assert isinstance(torrent.id, int) assert isinstance(torrent.name, str) def test_real_torrent_get_files(tr_client: Client): with open("tests/fixtures/iso.torrent", "rb") as f: tr_client.add_torrent(f) assert len(tr_client.get_torrents()) == 1, "transmission should has at least 1 task" for torrent in tr_client.get_torrents(): for file in torrent.get_files(): assert isinstance(file, File) @pytest.mark.parametrize( "status_code", [401, 403], ) def test_raise_unauthorized(status_code): m = mock.Mock(return_value=mock.Mock(status_code=status_code)) with mock.patch("requests.Session.post", m), pytest.raises(TransmissionAuthError): Client() def test_ensure_location_str_relative(): with pytest.raises(ValueError, match="relative"): ensure_location_str(pathlib.Path(".")) def test_ensure_location_str_absolute(): ensure_location_str(pathlib.Path(".").absolute()) transmission-rpc-7.0.3/tests/test_parse_id.py000066400000000000000000000017071451315226700214100ustar00rootroot00000000000000import pytest from transmission_rpc.client import _parse_torrent_id, _parse_torrent_ids example_hash = "51ba7d0dd45ab9b9564329c33f4f97493b677924" @pytest.mark.parametrize("arg", [float(1), "non-hash-string"]) def test_parse_id_raise(arg): with pytest.raises(ValueError, match=f"{arg} is not valid torrent id"): _parse_torrent_id(arg) @pytest.mark.parametrize( ("arg", "expected"), [ ("recently-active", "recently-active"), (example_hash, [example_hash]), ((2, example_hash), [2, example_hash]), (3, [3]), (None, []), ], ) def test_parse_torrent_ids(arg, expected): assert _parse_torrent_ids(arg) == expected, f"parse_torrent_ids({arg}) != {expected}" @pytest.mark.parametrize("arg", ["not-recently-active", "non-hash-string", -1, 1.1, "5:10", "5,6,8,9,10"]) def test_parse_torrent_ids_value_error(arg): with pytest.raises(ValueError, match="torrent id"): _parse_torrent_ids(arg) transmission-rpc-7.0.3/tests/test_torrent.py000066400000000000000000000067161451315226700213240ustar00rootroot00000000000000import time import calendar import datetime import pytz import pytest import transmission_rpc import transmission_rpc.utils import transmission_rpc.constants from transmission_rpc.torrent import Status def test_initial(): with pytest.raises(ValueError, match="Torrent object requires field 'id'"): transmission_rpc.Torrent(fields={}) transmission_rpc.Torrent(fields={"id": 42}) def assert_property_exception(exception, ob, prop): with pytest.raises(exception): getattr(ob, prop) def test_non_active(): data = { "id": 1, "activityDate": 0, } torrent = transmission_rpc.Torrent(fields=data) assert torrent.activity_date def test_attributes(): torrent = transmission_rpc.Torrent(fields={"id": 42}) assert torrent.id == 42 assert_property_exception(KeyError, torrent, "status") assert_property_exception(KeyError, torrent, "progress") assert_property_exception(KeyError, torrent, "ratio") assert_property_exception(KeyError, torrent, "eta") assert_property_exception(KeyError, torrent, "activity_date") assert_property_exception(KeyError, torrent, "added_date") assert_property_exception(KeyError, torrent, "start_date") assert_property_exception(KeyError, torrent, "done_date") with pytest.raises(KeyError): torrent.format_eta() assert not torrent.get_files() data = { "id": 1, "status": 4, "sizeWhenDone": 1000, "leftUntilDone": 500, "uploadedEver": 1000, "downloadedEver": 2000, "uploadRatio": 0.5, "eta": 3600, "percentDone": 0.5, "activityDate": calendar.timegm((2008, 12, 11, 11, 15, 30, 0, 0, -1)), "addedDate": calendar.timegm((2008, 12, 11, 8, 5, 10, 0, 0, -1)), "startDate": calendar.timegm((2008, 12, 11, 9, 10, 5, 0, 0, -1)), "doneDate": calendar.timegm((2008, 12, 11, 10, 0, 15, 0, 0, -1)), } torrent = transmission_rpc.Torrent(fields=data) assert torrent.id == 1 assert torrent.left_until_done == 500 assert torrent.status == "downloading" assert torrent.status.downloading assert torrent.progress == 50.0 assert torrent.ratio == 0.5 assert torrent.eta == datetime.timedelta(seconds=3600) assert torrent.activity_date == datetime.datetime(2008, 12, 11, 11, 15, 30, tzinfo=pytz.utc) assert torrent.added_date == datetime.datetime(2008, 12, 11, 8, 5, 10, tzinfo=pytz.utc) assert torrent.start_date == datetime.datetime(2008, 12, 11, 9, 10, 5, tzinfo=pytz.utc) assert torrent.done_date == datetime.datetime(2008, 12, 11, 10, 0, 15, tzinfo=pytz.utc) assert torrent.format_eta() == transmission_rpc.utils.format_timedelta(torrent.eta) data = { "id": 1, "status": 4, "sizeWhenDone": 1000, "leftUntilDone": 500, "uploadedEver": 1000, "downloadedEver": 2000, "uploadRatio": 0.5, "eta": 3600, "activityDate": time.mktime((2008, 12, 11, 11, 15, 30, 0, 0, -1)), "addedDate": time.mktime((2008, 12, 11, 8, 5, 10, 0, 0, -1)), "startDate": time.mktime((2008, 12, 11, 9, 10, 5, 0, 0, -1)), "doneDate": 0, } torrent = transmission_rpc.Torrent(fields=data) assert torrent.done_date is None def test_status(): assert Status("downloading").downloading assert not Status("downloading").download_pending assert Status("download pending").download_pending assert Status("download pending") in {"download pending", "o"} transmission-rpc-7.0.3/tests/test_utils.py000066400000000000000000000063711451315226700207640ustar00rootroot00000000000000# 2008-12, Erik Svensson # Copyright (c) 2018-2020 Trim21 # Licensed under the MIT license. import datetime from typing import Any, Dict, Tuple from unittest import mock import pytest from transmission_rpc import utils, from_url from transmission_rpc.constants import LOGGER, DEFAULT_TIMEOUT def assert_almost_eq(value: float, expected: float): assert abs(value - expected) < 1 @pytest.mark.parametrize( ("size", "expected"), { 512: (512, "B"), 1024: (1.0, "KiB"), 1048575: (1023.999, "KiB"), 1048576: (1.0, "MiB"), 1073741824: (1.0, "GiB"), 1099511627776: (1.0, "TiB"), 1125899906842624: (1.0, "PiB"), 1152921504606846976: (1.0, "EiB"), }.items(), ) def test_format_size(size, expected: Tuple[float, str]): result = utils.format_size(size) assert_almost_eq(result[0], expected[0]) assert result[1] == expected[1] @pytest.mark.parametrize( ("size", "expected"), [ (512, (512, "B/s")), (1024, (1.0, "KiB/s")), (1048575, (1023.999, "KiB/s")), (1048576, (1.0, "MiB/s")), (1073741824, (1.0, "GiB/s")), (1099511627776, (1.0, "TiB/s")), (1125899906842624, (1.0, "PiB/s")), (1152921504606846976, (1.0, "EiB/s")), ], ) def test_format_speed(size, expected): result = utils.format_speed(size) assert_almost_eq(result[0], expected[0]) assert result[1] == expected[1] @pytest.mark.parametrize( ("delta", "expected"), { datetime.timedelta(0, 0): "0 00:00:00", datetime.timedelta(0, 10): "0 00:00:10", datetime.timedelta(0, 60): "0 00:01:00", datetime.timedelta(0, 61): "0 00:01:01", datetime.timedelta(0, 3661): "0 01:01:01", datetime.timedelta(1, 3661): "1 01:01:01", datetime.timedelta(13, 65660): "13 18:14:20", }.items(), ) def test_format_timedelta(delta, expected): assert utils.format_timedelta(delta), expected @pytest.mark.parametrize( ("url", "kwargs"), { "http://a:b@127.0.0.1:9092/transmission/rpc": { "protocol": "http", "username": "a", "password": "b", "host": "127.0.0.1", "port": 9092, "path": "/transmission/rpc", }, "http://127.0.0.1/transmission/rpc": { "protocol": "http", "username": None, "password": None, "host": "127.0.0.1", "port": 80, "path": "/transmission/rpc", }, "https://127.0.0.1/tr/transmission/rpc": { "protocol": "https", "username": None, "password": None, "host": "127.0.0.1", "port": 443, "path": "/tr/transmission/rpc", }, "https://127.0.0.1/": { "protocol": "https", "username": None, "password": None, "host": "127.0.0.1", "port": 443, "path": "/", }, }.items(), ) def test_from_url(url: str, kwargs: Dict[str, Any]): with mock.patch("transmission_rpc.Client") as m: from_url(url) m.assert_called_once_with( **kwargs, timeout=DEFAULT_TIMEOUT, logger=LOGGER, ) transmission-rpc-7.0.3/transmission_rpc/000077500000000000000000000000001451315226700204375ustar00rootroot00000000000000transmission-rpc-7.0.3/transmission_rpc/__init__.py000066400000000000000000000035451451315226700225570ustar00rootroot00000000000000import logging import urllib.parse from transmission_rpc.error import TransmissionError from transmission_rpc.types import File, Group from transmission_rpc.client import Client from transmission_rpc.session import Session from transmission_rpc.torrent import Torrent from transmission_rpc.constants import LOGGER, DEFAULT_TIMEOUT, IdleMode, Priority, RatioLimitMode __all__ = [ "Client", "Group", "DEFAULT_TIMEOUT", "LOGGER", "TransmissionError", "Session", "Torrent", "File", "from_url", "Priority", "RatioLimitMode", "IdleMode", ] def from_url( url: str, timeout: float = DEFAULT_TIMEOUT, logger: logging.Logger = LOGGER, ) -> Client: """ .. code-block:: python from_url("http://127.0.0.1/transmission/rpc") # http://127.0.0.1:80/transmission/rpc from_url("https://127.0.0.1/transmission/rpc") # https://127.0.0.1:443/transmission/rpc from_url("http://127.0.0.1") # http://127.0.0.1:80/transmission/rpc from_url("http://127.0.0.1/") # http://127.0.0.1:80/ Warnings -------- you can't ignore scheme, ``127.0.0.1:9091`` is not valid url, please use ``http://127.0.0.1:9091`` And ``from_url("http://127.0.0.1")`` is not same as ``from_url("http://127.0.0.1/")``, ``path`` of ``http://127.0.0.1/`` is ``/`` """ u = urllib.parse.urlparse(url) protocol = u.scheme if protocol == "http": default_port = 80 elif protocol == "https": default_port = 443 else: raise ValueError(f"unknown url scheme {u.scheme}") return Client( protocol=protocol, # type: ignore username=u.username, password=u.password, host=u.hostname or "127.0.0.1", port=u.port or default_port, path=u.path or "/transmission/rpc", timeout=timeout, logger=logger, ) transmission-rpc-7.0.3/transmission_rpc/client.py000066400000000000000000001252321451315226700222740ustar00rootroot00000000000000import json import time import types import string import logging import pathlib import urllib.parse from typing import Any, Dict, List, Type, Tuple, Union, TypeVar, BinaryIO, Iterable, Optional from urllib.parse import quote import requests import requests.auth import requests.exceptions from typing_extensions import Literal, TypedDict from transmission_rpc.error import ( TransmissionError, TransmissionAuthError, TransmissionConnectError, TransmissionTimeoutError, ) from transmission_rpc.types import Group, _Timeout from transmission_rpc.utils import _try_read_torrent, get_torrent_arguments from transmission_rpc.session import Session, SessionStats from transmission_rpc.torrent import Torrent from transmission_rpc.constants import LOGGER, DEFAULT_TIMEOUT, RpcMethod valid_hash_char = string.digits + string.ascii_letters _TorrentID = Union[int, str] _TorrentIDs = Union[_TorrentID, List[_TorrentID], None] class ResponseData(TypedDict): arguments: Any tag: int result: str def ensure_location_str(s: Union[str, pathlib.Path]) -> str: if isinstance(s, pathlib.Path): if s.is_absolute(): return str(s) raise ValueError( "using relative `pathlib.Path` as remote path is not supported in v4.", ) return str(s) def _parse_torrent_id(raw_torrent_id: Union[int, str]) -> Union[int, str]: if isinstance(raw_torrent_id, int): if raw_torrent_id >= 0: return raw_torrent_id elif isinstance(raw_torrent_id, str): if len(raw_torrent_id) != 40 or (set(raw_torrent_id) - set(valid_hash_char)): raise ValueError(f"torrent ids {raw_torrent_id} is not valid torrent id") return raw_torrent_id raise ValueError(f"{raw_torrent_id} is not valid torrent id") def _parse_torrent_ids(args: Any) -> Union[str, List[Union[str, int]]]: if args is None: return [] if isinstance(args, int): return [_parse_torrent_id(args)] if isinstance(args, str): if args == "recently-active": return args return [_parse_torrent_id(args)] if isinstance(args, (list, tuple)): return [_parse_torrent_id(item) for item in args] raise ValueError(f"Invalid torrent id {args}") class Client: semver_version: Optional[str] # available in transmission>=4.0.0 def __init__( self, *, protocol: Literal["http", "https"] = "http", username: Optional[str] = None, password: Optional[str] = None, host: str = "127.0.0.1", port: int = 9091, path: str = "/transmission/rpc", timeout: float = DEFAULT_TIMEOUT, logger: logging.Logger = LOGGER, ): """ Parameters ---------- protocol username password host port path: rpc request target path, default ``/transmission/rpc`` timeout logger """ if isinstance(logger, logging.Logger): self.logger = logger else: raise TypeError( "logger must be instance of `logging.Logger`, default: logging.getLogger('transmission-rpc')" ) self._query_timeout: _Timeout = timeout username = quote(username or "", safe="$-_.+!*'(),;&=", encoding="utf8") if username else "" password = ":" + quote(password or "", safe="$-_.+!*'(),;&=", encoding="utf8") if password else "" auth = f"{username}{password}@" if (username or password) else "" if path == "/transmission/": path = "/transmission/rpc" url = urllib.parse.urlunparse((protocol, f"{auth}{host}:{port}", path, None, None, None)) self.url = str(url) self._sequence = 0 self.raw_session: Dict[str, Any] = {} self.session_id = "0" self.server_version: str = "(unknown)" self.protocol_version: int = 17 # default 17 self._http_session = requests.Session() self._http_session.trust_env = False self.get_session() self.torrent_get_arguments = get_torrent_arguments(self.rpc_version) @property def timeout(self) -> _Timeout: """ Get current timeout for HTTP queries. """ return self._query_timeout @timeout.setter def timeout(self, value: _Timeout) -> None: """ Set timeout for HTTP queries. """ if isinstance(value, (tuple, list)): if len(value) != 2: raise ValueError("timeout tuple can only include 2 numbers elements") for v in value: if not isinstance(v, (float, int)): raise TypeError("element of timeout tuple can only be int of float") self._query_timeout = (value[0], value[1]) # for type checker elif value is None: self._query_timeout = DEFAULT_TIMEOUT else: self._query_timeout = float(value) @timeout.deleter def timeout(self) -> None: """ Reset the HTTP query timeout to the default. """ self._query_timeout = DEFAULT_TIMEOUT @property def _http_header(self) -> Dict[str, str]: return {"x-transmission-session-id": self.session_id} def _http_query(self, query: dict, timeout: Optional[_Timeout] = None) -> str: """ Query Transmission through HTTP. """ request_count = 0 if timeout is None: timeout = self.timeout while True: if request_count >= 10: raise TransmissionError("too much request, try enable logger to see what happened") self.logger.debug( { "url": self.url, "headers": self._http_header, "data": query, "timeout": timeout, } ) request_count += 1 try: r = self._http_session.post( self.url, headers=self._http_header, json=query, timeout=timeout, ) except requests.exceptions.Timeout as e: raise TransmissionTimeoutError("timeout when connection to transmission daemon") from e except requests.exceptions.ConnectionError as e: raise TransmissionConnectError(f"can't connect to transmission daemon: {e!s}") from e self.session_id = r.headers.get("X-Transmission-Session-Id", "0") self.logger.debug(r.text) if r.status_code in {401, 403}: self.logger.debug(r.request.headers) raise TransmissionAuthError("transmission daemon require auth", original=r) if r.status_code != 409: return r.text def _request( self, method: RpcMethod, arguments: Optional[Dict[str, Any]] = None, ids: Optional[_TorrentIDs] = None, require_ids: bool = False, timeout: Optional[_Timeout] = None, ) -> dict: """ Send json-rpc request to Transmission using http POST """ if not isinstance(method, str): raise TypeError("request takes method as string") if arguments is None: arguments = {} if not isinstance(arguments, dict): raise TypeError("request takes arguments should be dict") ids = _parse_torrent_ids(ids) if len(ids) > 0: arguments["ids"] = ids elif require_ids: raise ValueError("request require ids") query = {"tag": self._sequence, "method": method, "arguments": arguments} self._sequence += 1 start = time.time() http_data = self._http_query(query, timeout) elapsed = time.time() - start self.logger.info("http request took %.3f s", elapsed) try: data: ResponseData = json.loads(http_data) except ValueError as error: self.logger.exception("Error: %s") self.logger.exception('Request: "%s"', query) self.logger.exception('HTTP data: "%s"', http_data) raise TransmissionError( "failed to parse response as json", method=method, argument=arguments, rawResponse=http_data ) from error self.logger.debug(json.dumps(data, indent=2)) if "result" not in data: raise TransmissionError( "Query failed, response data missing without result.", method=method, argument=arguments, response=data, rawResponse=http_data, ) if data["result"] != "success": raise TransmissionError( f'Query failed with result "{data["result"]}".', method=method, argument=arguments, response=data, rawResponse=http_data, ) res = data["arguments"] results = {} if method == RpcMethod.TorrentGet: return res if method == RpcMethod.TorrentAdd: item = None if "torrent-added" in res: item = res["torrent-added"] elif "torrent-duplicate" in res: item = res["torrent-duplicate"] if item: results[item["id"]] = Torrent(fields=item) else: raise TransmissionError( "Invalid torrent-add response.", method=method, argument=arguments, response=data, rawResponse=http_data, ) elif method == RpcMethod.SessionGet: self.raw_session.update(res) elif method == RpcMethod.SessionStats: # older versions of T has the return data in "session-stats" if "session-stats" in res: return res["session-stats"] return res elif method in ( RpcMethod.PortTest, RpcMethod.BlocklistUpdate, RpcMethod.FreeSpace, RpcMethod.TorrentRenamePath, ): return res else: return res return results def _update_server_version(self) -> None: """Decode the Transmission version string, if available.""" self.semver_version = self.raw_session.get("rpc-version-semver") self.server_version = self.raw_session["version"] self.protocol_version = self.raw_session["rpc-version"] @property def rpc_version(self) -> int: """Get the Transmission daemon RPC version.""" return self.protocol_version def _rpc_version_warning(self, required_version: int) -> None: """ Add a warning to the log if the Transmission RPC version is lower then the provided version. """ if self.rpc_version < required_version: self.logger.warning( "Using feature not supported by server. RPC version for server %d, feature introduced in %d.", self.rpc_version, required_version, ) def add_torrent( self, torrent: Union[BinaryIO, str, bytes, pathlib.Path], timeout: Optional[_Timeout] = None, *, download_dir: Optional[str] = None, files_unwanted: Optional[List[int]] = None, files_wanted: Optional[List[int]] = None, paused: Optional[bool] = None, peer_limit: Optional[int] = None, priority_high: Optional[List[int]] = None, priority_low: Optional[List[int]] = None, priority_normal: Optional[List[int]] = None, cookies: Optional[str] = None, labels: Optional[Iterable[str]] = None, bandwidthPriority: Optional[int] = None, ) -> Torrent: """ Add torrent to transfers list. ``torrent`` can be: - ``http://``, ``https://`` or ``magnet:`` URL - torrent file-like object in **binary mode** - bytes of torrent content - ``pathlib.Path`` for local torrent file, will be read and encoded as base64. Warnings -------- base64 string or ``file://`` protocol URL are not supported in v4. Parameters ---------- torrent: torrent to add timeout: request timeout bandwidthPriority: Priority for this transfer. cookies: One or more HTTP cookie(s). download_dir: The directory where the downloaded contents will be saved in. files_unwanted: A list of file id's that shouldn't be downloaded. files_wanted: A list of file id's that should be downloaded. paused: If ``True``, does not start the transfer when added. Magnet url will always start to downloading torrents. peer_limit: Maximum number of peers allowed. priority_high: A list of file id's that should have high priority. priority_low: A list of file id's that should have low priority. priority_normal: A list of file id's that should have normal priority. labels: Array of string labels. Add in rpc 17. """ if torrent is None: raise ValueError("add_torrent requires data or a URI.") if labels is not None: self._rpc_version_warning(17) kwargs: Dict[str, Any] = remove_unset_value( { "download-dir": download_dir, "files-unwanted": files_unwanted, "files-wanted": files_wanted, "paused": paused, "peer-limit": peer_limit, "priority-high": priority_high, "priority-low": priority_low, "priority-normal": priority_normal, "bandwidthPriority": bandwidthPriority, "cookies": cookies, "labels": list_or_none(labels), } ) torrent_data = _try_read_torrent(torrent) if torrent_data: kwargs["metainfo"] = torrent_data else: kwargs["filename"] = torrent return next(iter(self._request(RpcMethod.TorrentAdd, kwargs, timeout=timeout).values())) def remove_torrent(self, ids: _TorrentIDs, delete_data: bool = False, timeout: Optional[_Timeout] = None) -> None: """ remove torrent(s) with provided id(s). Local data will be removed by transmission daemon if ``delete_data`` is set to ``True``. """ self._request( RpcMethod.TorrentRemove, {"delete-local-data": delete_data}, ids, True, timeout=timeout, ) def start_torrent(self, ids: _TorrentIDs, bypass_queue: bool = False, timeout: Optional[_Timeout] = None) -> None: """Start torrent(s) with provided id(s)""" method = RpcMethod.TorrentStart if bypass_queue: method = RpcMethod.TorrentStartNow self._request(method, {}, ids, True, timeout=timeout) def start_all(self, bypass_queue: bool = False, timeout: Optional[_Timeout] = None) -> None: """Start all torrents respecting the queue order""" method = RpcMethod.TorrentStart if bypass_queue: method = RpcMethod.TorrentStartNow torrent_list = sorted(self.get_torrents(), key=lambda t: t.queue_position) self._request( method, {}, ids=[x.id for x in torrent_list], require_ids=True, timeout=timeout, ) def stop_torrent(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """stop torrent(s) with provided id(s)""" self._request(RpcMethod.TorrentStop, {}, ids, True, timeout=timeout) def verify_torrent(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """verify torrent(s) with provided id(s)""" self._request(RpcMethod.TorrentVerify, {}, ids, True, timeout=timeout) def reannounce_torrent(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """Reannounce torrent(s) with provided id(s)""" self._request(RpcMethod.TorrentReannounce, {}, ids, True, timeout=timeout) def get_torrent( self, torrent_id: _TorrentID, arguments: Optional[Iterable[str]] = None, timeout: Optional[_Timeout] = None, ) -> Torrent: """ Get information for torrent with provided id. ``arguments`` contains a list of field names to be returned, when None all fields are requested. See the Torrent class for more information. new argument ``format`` in rpc_version 16 is unnecessarily and this lib can't handle table response, So it's unsupported. Returns a Torrent object with the requested fields. Note ---- It's recommended that you use torrent's ``info_hash`` as torrent id. The torrent's ``info_hash`` will never change. Parameters ---------- torrent_id: torrent id can be an int or a torrent ``info_hash`` (``hashString`` property of the ``Torrent`` object). arguments: fetched torrent arguments, in most cases you don't need to set this, transmission-rpc will fetch all torrent fields it supported. timeout: requests timeout Raises ------ KeyError: torrent with given ``torrent_id`` not found """ if arguments: arguments = list(set(arguments) | {"id", "hashString"}) else: arguments = self.torrent_get_arguments torrent_id = _parse_torrent_id(torrent_id) if torrent_id is None: raise ValueError("Invalid id") result = self._request( RpcMethod.TorrentGet, {"fields": arguments}, torrent_id, require_ids=True, timeout=timeout, ) for torrent in result["torrents"]: if torrent.get("hashString") == torrent_id or torrent.get("id") == torrent_id: return Torrent(fields=torrent) raise KeyError("Torrent not found in result") def get_torrents( self, ids: Optional[_TorrentIDs] = None, arguments: Optional[Iterable[str]] = None, timeout: Optional[_Timeout] = None, ) -> List[Torrent]: """ Get information for torrents with provided ids. For more information see :py:meth:`Client.get_torrent`. Returns a list of Torrent object. """ if arguments: arguments = list(set(arguments) | {"id", "hashString"}) else: arguments = self.torrent_get_arguments return [ Torrent(fields=x) for x in self._request(RpcMethod.TorrentGet, {"fields": arguments}, ids, timeout=timeout)["torrents"] ] def get_recently_active_torrents( self, arguments: Optional[Iterable[str]] = None, timeout: Optional[_Timeout] = None ) -> Tuple[List[Torrent], List[int]]: """ Get information for torrents for recently active torrent. If you want to get recently-removed torrents. you should use this method. Returns ------- active_torrents: List[Torrent] List of recently active torrents removed_torrents: List[int] List of torrent-id of recently-removed torrents. """ if arguments: arguments = list(set(arguments) | {"id", "hashString"}) else: arguments = self.torrent_get_arguments result = self._request(RpcMethod.TorrentGet, {"fields": arguments}, "recently-active", timeout=timeout) return [Torrent(fields=x) for x in result["torrents"]], result["removed"] def change_torrent( self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None, *, bandwidth_priority: Optional[int] = None, download_limit: Optional[int] = None, download_limited: Optional[bool] = None, upload_limit: Optional[int] = None, upload_limited: Optional[bool] = None, files_unwanted: Optional[Iterable[int]] = None, files_wanted: Optional[Iterable[int]] = None, honors_session_limits: Optional[bool] = None, location: Optional[str] = None, peer_limit: Optional[int] = None, priority_high: Optional[Iterable[int]] = None, priority_low: Optional[Iterable[int]] = None, priority_normal: Optional[Iterable[int]] = None, queue_position: Optional[int] = None, seed_idle_limit: Optional[int] = None, seed_idle_mode: Optional[int] = None, seed_ratio_limit: Optional[float] = None, seed_ratio_mode: Optional[int] = None, tracker_add: Optional[Iterable[str]] = None, labels: Optional[Iterable[str]] = None, group: Optional[str] = None, tracker_list: Optional[Iterable[Iterable[str]]] = None, tracker_replace: Optional[Iterable[Tuple[int, str]]] = None, tracker_remove: Optional[Iterable[int]] = None, **kwargs: Any, ) -> None: """Change torrent parameters for the torrent(s) with the supplied id's. Parameters ---------- ids torrent(s) to change. timeout requesst timeout. honors_session_limits true if session upload limits are honored. location new location of the torrent's content peer_limit maximum number of peers queue_position position of this torrent in its queue [0...n) files_wanted Array of file id to download. files_unwanted Array of file id to not download. download_limit maximum download speed (KBps) download_limited true if ``download_limit`` is honored upload_limit maximum upload speed (KBps) upload_limited true if ``upload_limit`` is honored bandwidth_priority Priority for this transfer. priority_high list of file id to set high download priority priority_low list of file id to set low download priority priority_normal list of file id to set normal download priority seed_ratio_limit Seed inactivity limit in minutes. seed_ratio_mode Torrent seed ratio mode Valid options are :py:class:`transmission_rpc.constants.RatioLimitMode` seed_idle_limit torrent-level seeding ratio seed_idle_mode Seed inactivity mode. Valid options are :py:class:`transmission_rpc.constants.IdleMode` labels Array of string labels. Add in rpc 16. group The name of this torrent's bandwidth group. Add in rpc 17. tracker_list A ``Iterable[Iterable[str]]``, each ``Iterable[str]`` for a tracker tier. Add in rpc 17. Example: ``[['https://tracker1/announce', 'https://tracker2/announce'], ['https://backup1.example.com/announce'], ['https://backup2.example.com/announce']]``. tracker_add: Array of string with announce URLs to add. Warnings -------- since transmission daemon 4.0.0, this argument is deprecated, use ``tracker_list`` instead. tracker_remove: Array of ids of trackers to remove. Warnings -------- since transmission daemon 4.0.0, this argument is deprecated, use ``tracker_list`` instead. tracker_replace: Array of (id, url) tuples where the announcement URL should be replaced. Warnings -------- since transmission daemon 4.0.0, this argument is deprecated, use ``tracker_list`` instead. Warnings ---- ``kwargs`` is for the future features not supported yet, it's not compatibility promising. It will be bypassed to request arguments **as-is**, the underline in the key will not be replaced, so you should use kwargs like ``{'a-argument': 'value'}`` """ if labels is not None: self._rpc_version_warning(16) if tracker_list is not None: self._rpc_version_warning(17) if group is not None: self._rpc_version_warning(17) args: Dict[str, Any] = remove_unset_value( { "bandwidthPriority": bandwidth_priority, "downloadLimit": download_limit, "downloadLimited": download_limited, "uploadLimit": upload_limit, "uploadLimited": upload_limited, "files-unwanted": list_or_none(files_unwanted), "files-wanted": list_or_none(files_wanted), "honorsSessionLimits": honors_session_limits, "location": location, "peer-limit": peer_limit, "priority-high": list_or_none(priority_high), "priority-low": list_or_none(priority_low), "priority-normal": list_or_none(priority_normal), "queuePosition": queue_position, "seedIdleLimit": seed_idle_limit, "seedIdleMode": seed_idle_mode, "seedRatioLimit": seed_ratio_limit, "seedRatioMode": seed_ratio_mode, "trackerAdd": tracker_add, "trackerRemove": tracker_remove, "trackerReplace": tracker_replace, "labels": list_or_none(labels), "trackerList": None if tracker_list is None else "\n".join("\n\n".join(x) for x in tracker_list), "group": group, } ) args.update(kwargs) if args: self._request(RpcMethod.TorrentSet, args, ids, True, timeout=timeout) else: ValueError("No arguments to set") def move_torrent_data( self, ids: _TorrentIDs, location: Union[str, pathlib.Path], timeout: Optional[_Timeout] = None, *, move: bool = True, ) -> None: """ Move torrent data to the new location. See Also -------- `RPC Spec: moving-a-torrent `_ """ args = {"location": ensure_location_str(location), "move": bool(move)} self._request(RpcMethod.TorrentSetLocation, args, ids, True, timeout=timeout) def rename_torrent_path( self, torrent_id: _TorrentID, location: str, name: str, timeout: Optional[_Timeout] = None, ) -> Tuple[str, str]: """ Warnings -------- This method can only be called on single torrent. Warnings -------- This is not the method to move torrent data directory, See Also -------- `RPC Spec: renaming-a-torrents-path `_ """ self._rpc_version_warning(15) torrent_id = _parse_torrent_id(torrent_id) name = name.strip() # https://github.com/trim21/transmission-rpc/issues/185 result = self._request( RpcMethod.TorrentRenamePath, {"path": ensure_location_str(location), "name": name}, torrent_id, True, timeout=timeout, ) return result["path"], result["name"] def queue_top(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """ Move transfer to the top of the queue. https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#46-queue-movement-requests """ self._request(RpcMethod.QueueMoveTop, ids=ids, require_ids=True, timeout=timeout) def queue_bottom(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """ Move transfer to the bottom of the queue. https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#46-queue-movement-requests """ self._request(RpcMethod.QueueMoveBottom, ids=ids, require_ids=True, timeout=timeout) def queue_up(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """Move transfer up in the queue.""" self._request(RpcMethod.QueueMoveUp, ids=ids, require_ids=True, timeout=timeout) def queue_down(self, ids: _TorrentIDs, timeout: Optional[_Timeout] = None) -> None: """Move transfer down in the queue.""" self._request(RpcMethod.QueueMoveDown, ids=ids, require_ids=True, timeout=timeout) def get_session(self, timeout: Optional[_Timeout] = None) -> Session: """ Get session parameters. See the Session class for more information. """ self._request(RpcMethod.SessionGet, timeout=timeout) self._update_server_version() return Session(fields=self.raw_session) def set_session( self, timeout: Optional[_Timeout] = None, *, alt_speed_down: Optional[int] = None, alt_speed_enabled: Optional[bool] = None, alt_speed_time_begin: Optional[int] = None, alt_speed_time_day: Optional[int] = None, alt_speed_time_enabled: Optional[bool] = None, alt_speed_time_end: Optional[int] = None, alt_speed_up: Optional[int] = None, blocklist_enabled: Optional[bool] = None, blocklist_url: Optional[str] = None, cache_size_mb: Optional[int] = None, dht_enabled: Optional[bool] = None, default_trackers: Optional[Iterable[str]] = None, download_dir: Optional[str] = None, download_queue_enabled: Optional[bool] = None, download_queue_size: Optional[int] = None, encryption: Optional[Literal["required", "preferred", "tolerated"]] = None, idle_seeding_limit: Optional[int] = None, idle_seeding_limit_enabled: Optional[bool] = None, incomplete_dir: Optional[str] = None, incomplete_dir_enabled: Optional[bool] = None, lpd_enabled: Optional[bool] = None, peer_limit_global: Optional[int] = None, peer_limit_per_torrent: Optional[int] = None, peer_port: Optional[int] = None, peer_port_random_on_start: Optional[bool] = None, pex_enabled: Optional[bool] = None, port_forwarding_enabled: Optional[bool] = None, queue_stalled_enabled: Optional[bool] = None, queue_stalled_minutes: Optional[int] = None, rename_partial_files: Optional[bool] = None, script_torrent_done_enabled: Optional[bool] = None, script_torrent_done_filename: Optional[str] = None, seed_queue_enabled: Optional[bool] = None, seed_queue_size: Optional[int] = None, seed_ratio_limit: Optional[int] = None, seed_ratio_limited: Optional[bool] = None, speed_limit_down: Optional[int] = None, speed_limit_down_enabled: Optional[bool] = None, speed_limit_up: Optional[int] = None, speed_limit_up_enabled: Optional[bool] = None, start_added_torrents: Optional[bool] = None, trash_original_torrent_files: Optional[bool] = None, utp_enabled: Optional[bool] = None, script_torrent_done_seeding_filename: Optional[str] = None, script_torrent_done_seeding_enabled: Optional[bool] = None, script_torrent_added_enabled: Optional[bool] = None, script_torrent_added_filename: Optional[str] = None, **kwargs: Any, ) -> None: """ Set session parameters. Parameters ---------- timeout request timeout alt_speed_down: max global download speed (KBps) alt_speed_enabled: true means use the alt speeds alt_speed_time_begin: Time when alternate speeds should be enabled. Minutes after midnight. alt_speed_time_day: Enables alternate speeds scheduling these days. alt_speed_time_enabled: Enables alternate speeds scheduling. alt_speed_time_end: Time when alternate speeds should be disabled. Minutes after midnight. alt_speed_up: Alternate session upload speed limit (in Kib/s). blocklist_enabled: Enables the block list blocklist_url: Location of the block list. Updated with blocklist-update. cache_size_mb: The maximum size of the disk cache in MB default_trackers: List of default trackers to use on public torrents. dht_enabled: Enables DHT. download_dir: Set the session download directory. download_queue_enabled: Enables download queue. download_queue_size: Number of slots in the download queue. encryption: Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``. idle_seeding_limit: The default seed inactivity limit in minutes. idle_seeding_limit_enabled: Enables the default seed inactivity limit incomplete_dir: The path to the directory of incomplete transfer data. incomplete_dir_enabled: Enables the incomplete transfer data directory, Otherwise data for incomplete transfers are stored in the download target. lpd_enabled: Enables local peer discovery for public torrents. peer_limit_global: Maximum number of peers. peer_limit_per_torrent: Maximum number of peers per transfer. peer_port: Peer port. peer_port_random_on_start: Enables randomized peer port on start of Transmission. pex_enabled: Allowing PEX in public torrents. port_forwarding_enabled: Enables port forwarding. queue_stalled_enabled: Enable tracking of stalled transfers. queue_stalled_minutes: Number of minutes of idle that marks a transfer as stalled. rename_partial_files: Appends ".part" to incomplete files seed_queue_enabled: Enables upload queue. seed_queue_size: Number of slots in the upload queue. seed_ratio_limit: Seed ratio limit. 1.0 means 1:1 download and upload ratio. seed_ratio_limited: Enables seed ration limit. speed_limit_down: Download speed limit (in Kib/s). speed_limit_down_enabled: Enables download speed limiting. speed_limit_up: Upload speed limit (in Kib/s). speed_limit_up_enabled: Enables upload speed limiting. start_added_torrents: Added torrents will be started right away. trash_original_torrent_files: The .torrent file of added torrents will be deleted. utp_enabled: Enables Micro Transport Protocol (UTP). script_torrent_done_enabled: Whether to call the "done" script. script_torrent_done_filename: Filename of the script to run when the transfer is done. script_torrent_added_filename: filename of the script to run script_torrent_added_enabled: whether or not to call the ``added`` script script_torrent_done_seeding_enabled: whether or not to call the ``seeding-done`` script script_torrent_done_seeding_filename: filename of the script to run Warnings ---- ``kwargs`` is pass the arguments not supported yet future, it's not compatibility promising. transmission-rpc will merge ``kwargs`` in rpc arguments *as-is* """ if encryption is not None and encryption not in ["required", "preferred", "tolerated"]: raise ValueError("Invalid encryption value") if default_trackers is not None: self._rpc_version_warning(17) if script_torrent_done_seeding_filename is not None: self._rpc_version_warning(17) if script_torrent_done_seeding_enabled is not None: self._rpc_version_warning(17) if script_torrent_added_enabled is not None: self._rpc_version_warning(17) if script_torrent_added_filename is not None: self._rpc_version_warning(17) args: Dict[str, Any] = remove_unset_value( { "alt-speed-down": alt_speed_down, "alt-speed-enabled": alt_speed_enabled, "alt-speed-time-begin": alt_speed_time_begin, "alt-speed-time-day": alt_speed_time_day, "alt-speed-time-enabled": alt_speed_time_enabled, "alt-speed-time-end": alt_speed_time_end, "alt-speed-up": alt_speed_up, "blocklist-enabled": blocklist_enabled, "blocklist-url": blocklist_url, "cache-size-mb": cache_size_mb, "dht-enabled": dht_enabled, "download-dir": download_dir, "download-queue-enabled": download_queue_enabled, "download-queue-size": download_queue_size, "idle-seeding-limit-enabled": idle_seeding_limit_enabled, "idle-seeding-limit": idle_seeding_limit, "incomplete-dir": incomplete_dir, "incomplete-dir-enabled": incomplete_dir_enabled, "lpd-enabled": lpd_enabled, "peer-limit-global": peer_limit_global, "peer-limit-per-torrent": peer_limit_per_torrent, "peer-port-random-on-start": peer_port_random_on_start, "peer-port": peer_port, "pex-enabled": pex_enabled, "port-forwarding-enabled": port_forwarding_enabled, "queue-stalled-enabled": queue_stalled_enabled, "queue-stalled-minutes": queue_stalled_minutes, "rename-partial-files": rename_partial_files, "script-torrent-done-enabled": script_torrent_done_enabled, "script-torrent-done-filename": script_torrent_done_filename, "seed-queue-enabled": seed_queue_enabled, "seed-queue-size": seed_queue_size, "seedRatioLimit": seed_ratio_limit, "seedRatioLimited": seed_ratio_limited, "speed-limit-down": speed_limit_down, "speed-limit-down-enabled": speed_limit_down_enabled, "speed-limit-up": speed_limit_up, "speed-limit-up-enabled": speed_limit_up_enabled, "start-added-torrents": start_added_torrents, "trash-original-torrent-files": trash_original_torrent_files, "utp-enabled": utp_enabled, "encryption": encryption, "script-torrent-added-filename": script_torrent_added_filename, "script-torrent-done-seeding-filename": script_torrent_done_seeding_filename, "script-torrent-done-seeding-enabled": script_torrent_done_seeding_enabled, "script-torrent-added-enabled": script_torrent_added_enabled, "default-trackers": "\n".join(default_trackers) if default_trackers is not None else None, } ) args.update(kwargs) if args: self._request(RpcMethod.SessionSet, args, timeout=timeout) def blocklist_update(self, timeout: Optional[_Timeout] = None) -> Optional[int]: """Update block list. Returns the size of the block list.""" result = self._request(RpcMethod.BlocklistUpdate, timeout=timeout) return result.get("blocklist-size") def port_test(self, timeout: Optional[_Timeout] = None) -> Optional[bool]: """ Tests to see if your incoming peer port is accessible from the outside world. """ result = self._request(RpcMethod.PortTest, timeout=timeout) return result.get("port-is-open") def free_space(self, path: Union[str, pathlib.Path], timeout: Optional[_Timeout] = None) -> Optional[int]: """ Get the amount of free space (in bytes) at the provided location. """ self._rpc_version_warning(15) path = ensure_location_str(path) result: Dict[str, Any] = self._request(RpcMethod.FreeSpace, {"path": path}, timeout=timeout) if result["path"] == path: return result["size-bytes"] return None def session_stats(self, timeout: Optional[_Timeout] = None) -> SessionStats: """Get session statistics""" result = self._request(RpcMethod.SessionStats, timeout=timeout) return SessionStats(fields=result) def set_group( self, name: str, *, timeout: Optional[_Timeout] = None, honors_session_limits: Optional[bool] = None, speed_limit_down: Optional[int] = None, speed_limit_up_enabled: Optional[bool] = None, speed_limit_up: Optional[int] = None, speed_limit_down_enabled: Optional[bool] = None, ) -> None: self._rpc_version_warning(17) arguments: Dict[str, Any] = remove_unset_value( { "name": name, "honorsSessionLimits": honors_session_limits, "speed-limit-down": speed_limit_down, "speed-limit-up-enabled": speed_limit_up_enabled, "speed-limit-up": speed_limit_up, "speed-limit-down-enabled": speed_limit_down_enabled, } ) self._request(RpcMethod.GroupSet, arguments, timeout=timeout) def get_group(self, name: str, *, timeout: Optional[_Timeout] = None) -> Optional[Group]: self._rpc_version_warning(17) result: Dict[str, Any] = self._request(RpcMethod.GroupGet, {"group": name}, timeout=timeout) if result["arguments"]["group"]: return Group(fields=result["arguments"]["group"][0]) return None def get_groups(self, name: Optional[List[str]] = None, *, timeout: Optional[_Timeout] = None) -> Dict[str, Group]: payload = {} if name is not None: payload = {"group": name} result: Dict[str, Any] = self._request(RpcMethod.GroupGet, payload, timeout=timeout) return {x["name"]: Group(fields=x) for x in result["arguments"]["group"]} def __enter__(self) -> "Client": return self def __exit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[types.TracebackType], ) -> None: self._http_session.close() T = TypeVar("T") def list_or_none(v: Optional[Iterable[T]]) -> Optional[List[T]]: if v is None: return None return list(v) def remove_unset_value(data: Dict[str, Any]) -> Dict[str, Any]: return {key: value for key, value in data.items() if value is not None} transmission-rpc-7.0.3/transmission_rpc/constants.py000066400000000000000000000235251451315226700230340ustar00rootroot00000000000000# Copyright (c) 2018-2022 Trim21 # Copyright (c) 2008-2014 Erik Svensson # Licensed under the MIT license. import enum import logging from typing import Dict, Optional, NamedTuple LOGGER = logging.getLogger("transmission-rpc") LOGGER.setLevel(logging.ERROR) DEFAULT_TIMEOUT = 30.0 class Priority(enum.IntEnum): Low = -1 Normal = 0 High = 1 class RatioLimitMode(enum.IntEnum): """torrent radio limit mode""" #: follow the global settings Global = 0 #: override the global settings, seeding until a certain ratio Single = 1 #: override the global settings, seeding regardless of ratio Unlimited = 2 class IdleMode(enum.IntEnum): """torrent idle mode""" #: follow the global settings Global = 0 #: override the global settings, seeding until a certain idle time Single = 1 #: override the global settings, seeding regardless of activity Unlimited = 2 class Args(NamedTuple): type: str added_version: int removed_version: Optional[int] = None previous_argument_name: Optional[str] = None next_argument_name: Optional[str] = None description: str = "" def __repr__(self) -> str: return ( f"Args({self.type!r}," f" {self.added_version!r}," f" {self.removed_version!r}," f" {self.previous_argument_name!r}," f" {self.next_argument_name!r}," f" {self.description!r})" ) def __str__(self) -> str: return f"Args str: if self.original: original_name = type(self.original).__name__ return f'{self.message} Original exception: {original_name}, "{self.original}"' return self.message class TransmissionAuthError(TransmissionError): """Raised when username or password is incorrect""" class TransmissionConnectError(TransmissionError): """raised when client can't connect to transmission daemon""" class TransmissionTimeoutError(TransmissionConnectError): """Timeout""" transmission-rpc-7.0.3/transmission_rpc/py.typed000066400000000000000000000000001451315226700221240ustar00rootroot00000000000000transmission-rpc-7.0.3/transmission_rpc/session.py000066400000000000000000000303151451315226700224760ustar00rootroot00000000000000from typing import List, Optional from typing_extensions import Literal from transmission_rpc.types import Container class Stats(Container): @property def uploaded_bytes(self) -> int: return self.fields["uploadedBytes"] @property def downloaded_bytes(self) -> int: return self.fields["downloadedBytes"] @property def files_added(self) -> int: return self.fields["filesAdded"] @property def session_count(self) -> int: return self.fields["sessionCount"] @property def seconds_active(self) -> int: return self.fields["secondsActive"] class SessionStats(Container): # https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md # 42-session-statistics @property def active_torrent_count(self) -> int: return self.fields["activeTorrentCount"] @property def download_speed(self) -> int: return self.fields["downloadSpeed"] @property def paused_torrent_count(self) -> int: return self.fields["pausedTorrentCount"] @property def torrent_count(self) -> int: return self.fields["torrentCount"] @property def upload_speed(self) -> int: return self.fields["uploadSpeed"] @property def cumulative_stats(self) -> Stats: return Stats(fields=self.fields["cumulative-stats"]) @property def current_stats(self) -> Stats: return Stats(fields=self.fields["current-stats"]) class Units(Container): # 4 strings: KB/s, MB/s, GB/s, TB/s @property def speed_units(self) -> List[str]: return self.fields["speed-units"] # number of bytes in a KB (1000 for kB; 1024 for KiB) @property def speed_bytes(self) -> int: return self.fields["speed-bytes"] # 4 strings: KB/s, MB/s, GB/s, TB/s @property def size_units(self) -> List[str]: return self.fields["size-units"] # number of bytes in a KB (1000 for kB; 1024 for KiB) @property def size_bytes(self) -> int: return self.fields["size-bytes"] # 4 strings: KB/s, MB/s, GB/s, TB/s @property def memory_units(self) -> List[str]: return self.fields["memory-units"] # number of bytes in a KB (1000 for kB; 1024 for KiB) @property def memory_bytes(self) -> int: return self.fields["memory-bytes"] class Session(Container): """ Session is a class holding the session data for a Transmission daemon. Access the session field can be done through attributes. The attributes available are the same as the session arguments in the Transmission RPC specification, but with underscore instead of hyphen. get ``'download-dir'`` with ``session.download_dir``. .. code-block:: python session = Client().get_session() current = session.download_dir https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md#41-session-arguments Warnings -------- setter on session's properties has been removed, please use ``Client().set_session()`` instead """ @property def alt_speed_down(self) -> int: """max global download speed (KBps)""" return self.fields["alt-speed-down"] @property def alt_speed_enabled(self) -> bool: # true means use the alt speeds return self.fields["alt-speed-enabled"] @property def alt_speed_time_begin(self) -> int: """when to turn on alt speeds (units: minutes after midnight)""" return self.fields["alt-speed-time-begin"] @property def alt_speed_time_day(self) -> int: """what day(s) to turn on alt speeds (look at tr_sched_day)""" return self.fields["alt-speed-time-day"] @property def alt_speed_time_enabled(self) -> bool: """true means the scheduled on/off times are used""" return self.fields["alt-speed-time-enabled"] @property def alt_speed_time_end(self) -> int: """when to turn off alt speeds (units: same)""" return self.fields["alt-speed-time-end"] @property def alt_speed_up(self) -> int: """max global upload speed (KBps)""" return self.fields["alt-speed-up"] @property def blocklist_enabled(self) -> bool: """true means enabled""" return self.fields["blocklist-enabled"] @property def blocklist_size(self) -> int: """int of rules in the blocklist""" return self.fields["blocklist-size"] @property def blocklist_url(self) -> str: """location of the blocklist to use for `blocklist-update`""" return self.fields["blocklist-url"] @property def cache_size_mb(self) -> int: """maximum size of the disk cache (MB)""" return self.fields["cache-size-mb"] @property def config_dir(self) -> str: """location of transmission's configuration directory""" return self.fields["config-dir"] @property def dht_enabled(self) -> bool: """true means allow dht in public torrents""" return self.fields["dht-enabled"] @property def download_dir(self) -> str: """default path to download torrents""" return self.fields["download-dir"] @property def download_dir_free_space(self) -> int: """**DEPRECATED** Use the `free-space` method instead.""" return self.fields["download-dir-free-space"] @property def download_queue_enabled(self) -> bool: """if true, limit how many torrents can be downloaded at once""" return self.fields["download-queue-enabled"] @property def download_queue_size(self) -> int: """max int of torrents to download at once (see download-queue-enabled)""" return self.fields["download-queue-size"] @property def encryption(self) -> Literal["required", "preferred", "tolerated"]: return self.fields["encryption"] @property def idle_seeding_limit_enabled(self) -> bool: """true if the seeding inactivity limit is honored by default""" return self.fields["idle-seeding-limit-enabled"] @property def idle_seeding_limit(self) -> int: """torrents we're seeding will be stopped if they're idle for this long""" return self.fields["idle-seeding-limit"] @property def incomplete_dir_enabled(self) -> bool: """true means keep torrents in incomplete-dir until done""" return self.fields["incomplete-dir-enabled"] @property def incomplete_dir(self) -> str: """path for incomplete torrents, when enabled""" return self.fields["incomplete-dir"] @property def lpd_enabled(self) -> bool: """true means allow Local Peer Discovery in public torrents""" return self.fields["lpd-enabled"] @property def peer_limit_global(self) -> int: """maximum global int of peers""" return self.fields["peer-limit-global"] @property def peer_limit_per_torrent(self) -> int: """maximum global int of peers""" return self.fields["peer-limit-per-torrent"] @property def peer_port_random_on_start(self) -> bool: """true means pick a random peer port on launch""" return self.fields["peer-port-random-on-start"] @property def peer_port(self) -> int: """port int""" return self.fields["peer-port"] @property def pex_enabled(self) -> bool: """true means allow pex in public torrents""" return self.fields["pex-enabled"] @property def port_forwarding_enabled(self) -> bool: """true means ask upstream router to forward the configured peer port to transmission using UPnP or NAT-PMP""" return self.fields["port-forwarding-enabled"] @property def queue_stalled_enabled(self) -> bool: """whether or not to consider idle torrents as stalled""" return self.fields["queue-stalled-enabled"] @property def queue_stalled_minutes(self) -> int: """torrents that are idle for N minutes aren't counted toward seed-queue-size or download-queue-size""" return self.fields["queue-stalled-minutes"] @property def rename_partial_files(self) -> bool: """true means append `.part` to incomplete files""" return self.fields["rename-partial-files"] @property def rpc_version_minimum(self) -> int: """the minimum RPC API version supported""" return self.fields["rpc-version-minimum"] @property def rpc_version(self) -> int: """the current RPC API version""" return self.fields["rpc-version"] @property def script_torrent_done_enabled(self) -> bool: """whether or not to call the `done` script""" return self.fields["script-torrent-done-enabled"] @property def script_torrent_done_filename(self) -> str: """filename of the script to run""" return self.fields["script-torrent-done-filename"] @property def seed_queue_enabled(self) -> bool: """if true, limit how many torrents can be uploaded at once""" return self.fields["seed-queue-enabled"] @property def seed_queue_size(self) -> int: """max int of torrents to uploaded at once (see seed-queue-enabled)""" return self.fields["seed-queue-size"] @property def seed_ratio_limit(self) -> float: """the default seed ratio for torrents to use""" return self.fields["seedRatioLimit"] @property def seed_ratio_limited(self) -> bool: """true if seedRatioLimit is honored by default""" return self.fields["seedRatioLimited"] @property def speed_limit_down_enabled(self) -> bool: """true means enabled""" return self.fields["speed-limit-down-enabled"] @property def speed_limit_down(self) -> int: """max global download speed (KBps)""" return self.fields["speed-limit-down"] @property def speed_limit_up_enabled(self) -> bool: """true means enabled""" return self.fields["speed-limit-up-enabled"] @property def speed_limit_up(self) -> int: """max global upload speed (KBps)""" return self.fields["speed-limit-up"] @property def start_added_torrents(self) -> bool: """true means added torrents will be started right away""" return self.fields["start-added-torrents"] @property def trash_original_torrent_files(self) -> bool: """true means the .torrent file of added torrents will be deleted""" return self.fields["trash-original-torrent-files"] # see below @property def units(self) -> Units: return self.fields["units"] @property def utp_enabled(self) -> bool: """true means allow utp""" return self.fields["utp-enabled"] @property def version(self) -> str: """long version str `$version ($revision)`""" return self.fields["version"] @property def default_trackers(self) -> Optional[list]: """ list of default trackers to use on public torrents new at rpc-version 17 """ trackers = self.get("default-trackers") if trackers: return trackers.split("\n") return None @property def rpc_version_semver(self) -> Optional[str]: """ the current RPC API version in a semver-compatible str new at rpc-version 17 """ return self.get("rpc-version-semver") @property def script_torrent_added_enabled(self) -> Optional[bool]: """ whether or not to call the `added` script new at rpc-version 17 """ return self.get("script-torrent-added-enabled") @property def script_torrent_added_filename(self) -> Optional[str]: """ filename of the script to run new at rpc-version 17 """ return self.get("script-torrent-added-filename") @property def script_torrent_done_seeding_enabled(self) -> Optional[bool]: """ whether or not to call the `seeding-done` script new at rpc-version 17 """ return self.get("script-torrent-done-seeding-enabled") @property def script_torrent_done_seeding_filename(self) -> Optional[str]: """ filename of the script to run new at rpc-version 17 """ return self.get("script-torrent-done-seeding-filename") transmission-rpc-7.0.3/transmission_rpc/torrent.py000066400000000000000000000552431451315226700225170ustar00rootroot00000000000000import enum from typing import Any, Dict, List, Optional from datetime import datetime, timezone, timedelta from transmission_rpc.types import File, Container from transmission_rpc.utils import format_timedelta from transmission_rpc.constants import IdleMode, Priority, RatioLimitMode _STATUS_NEW_MAPPING = { 0: "stopped", 1: "check pending", 2: "checking", 3: "download pending", 4: "downloading", 5: "seed pending", 6: "seeding", } def get_status(code: int) -> str: """Get the torrent status using new status codes""" return _STATUS_NEW_MAPPING.get(code) or f"unknown status {code}" class Status(str, enum.Enum): """enum for torrent status""" STOPPED = "stopped" CHECK_PENDING = "check pending" CHECKING = "checking" DOWNLOAD_PENDING = "download pending" DOWNLOADING = "downloading" SEED_PENDING = "seed pending" SEEDING = "seeding" @property def stopped(self) -> bool: return self == "stopped" @property def check_pending(self) -> bool: return self == "check pending" @property def checking(self) -> bool: return self == "checking" @property def download_pending(self) -> bool: return self == "download pending" @property def downloading(self) -> bool: return self == "downloading" @property def seed_pending(self) -> bool: return self == "seed pending" @property def seeding(self) -> bool: return self == "seeding" def __str__(self) -> str: return self.value class FileStat(Container): @property def bytesCompleted(self) -> int: return self.fields["bytesCompleted"] @property def wanted(self) -> int: return self.fields["wanted"] @property def priority(self) -> int: return self.fields["priority"] class Tracker(Container): @property def id(self) -> int: return self.fields["id"] @property def announce(self) -> str: return self.fields["announce"] @property def scrape(self) -> str: return self.fields["scrape"] @property def tier(self) -> int: return self.fields["tier"] class TrackerStats(Container): @property def id(self) -> int: return self.fields["id"] @property def announce_state(self) -> int: return self.fields["announceState"] @property def announce(self) -> str: return self.fields["announce"] @property def download_count(self) -> int: return self.fields["downloadCount"] @property def has_announced(self) -> bool: return self.fields["hasAnnounced"] @property def has_scraped(self) -> bool: return self.fields["hasScraped"] @property def host(self) -> str: return self.fields["host"] @property def is_backup(self) -> bool: return self.fields["isBackup"] @property def last_announce_peer_count(self) -> int: return self.fields["lastAnnouncePeerCount"] @property def last_announce_result(self) -> str: return self.fields["lastAnnounceResult"] @property def last_announce_start_time(self) -> int: return self.fields["lastAnnounceStartTime"] @property def last_announce_succeeded(self) -> bool: return self.fields["lastAnnounceSucceeded"] @property def last_announce_time(self) -> int: return self.fields["lastAnnounceTime"] @property def last_announce_timed_out(self) -> bool: return self.fields["lastAnnounceTimedOut"] @property def last_scrape_result(self) -> str: return self.fields["lastScrapeResult"] @property def last_scrape_start_time(self) -> int: return self.fields["lastScrapeStartTime"] @property def last_scrape_succeeded(self) -> bool: return self.fields["lastScrapeSucceeded"] @property def last_scrape_time(self) -> int: return self.fields["lastScrapeTime"] @property def last_scrape_timed_out(self) -> bool: return self.fields["lastScrapeTimedOut"] @property def leecher_count(self) -> int: return self.fields["leecherCount"] @property def next_announce_time(self) -> int: return self.fields["nextAnnounceTime"] @property def next_scrape_time(self) -> int: return self.fields["nextScrapeTime"] @property def scrape_state(self) -> int: return self.fields["scrapeState"] @property def scrape(self) -> str: return self.fields["scrape"] @property def seeder_count(self) -> int: return self.fields["seederCount"] @property def site_name(self) -> str: return self.fields["sitename"] @property def tier(self) -> int: return self.fields["tier"] class Torrent(Container): """ Torrent is a class holding the data received from Transmission regarding a bittorrent transfer. Warnings -------- setter on Torrent's properties has been removed, please use ``Client().change_torrent()`` instead """ def __init__(self, *, fields: Dict[str, Any]): if "id" not in fields: raise ValueError( "Torrent object requires field 'id', " "you need to add 'id' in your 'arguments' when calling 'get_torrent'" ) super().__init__(fields=fields) @property def id(self) -> int: return self.fields["id"] @property def name(self) -> str: return self.fields["name"] @property def hashString(self) -> str: """Torrent info hash string, can also be used as Torrent ID""" return self.fields["hashString"] @property def available(self) -> float: """Availability in percent""" bytes_all = self.total_size bytes_done = sum(x["bytesCompleted"] for x in self.fields["fileStats"]) bytes_avail = self.desired_available + bytes_done return (bytes_avail / bytes_all) * 100 if bytes_all else 0 # @property # def availability(self) -> list: # """TODO""" # return self.fields["availability"] @property def bandwidth_priority(self) -> Priority: """this torrent's bandwidth priority""" return Priority(self.fields["bandwidthPriority"]) @property def comment(self) -> str: return self.fields["comment"] @property def corrupt_ever(self) -> int: """ Byte count of all the corrupt data you've ever downloaded for this torrent. If you're on a poisoned torrent, this number can grow very large. """ return self.fields["corruptEver"] @property def creator(self) -> str: return self.fields["creator"] # TODO # @property # def date_created(self): # return self.fields["dateCreated"] @property def desired_available(self) -> int: """ Byte count of all the piece data we want and don't have yet, but that a connected peer does have. [0...leftUntilDone] """ return self.fields["desiredAvailable"] @property def download_dir(self) -> Optional[str]: """The download directory. :available: transmission version 1.5. :available: RPC version 4. """ return self.fields["downloadDir"] @property def downloaded_ever(self) -> int: """ Byte count of all the non-corrupt data you've ever downloaded for this torrent. If you deleted the files and downloaded a second time, this will be 2*totalSize. """ return self.fields["downloadedEver"] @property def download_limit(self) -> int: return self.fields["downloadLimit"] @property def download_limited(self) -> bool: return self.fields["downloadLimited"] @property def edit_date(self) -> datetime: """ The last time during this session that a rarely-changing field changed -- e.g. any tr_torrent_metainfo field (trackers, filenames, name) or download directory. RPC clients can monitor this to know when to reload fields that rarely change. """ return datetime.fromtimestamp(self.fields["editDate"], timezone.utc) @property def error(self) -> int: """``0`` for fine task, non-zero for error torrent""" return self.fields["error"] @property def error_string(self) -> str: """empty string for fine task""" return self.fields["errorString"] @property def eta(self) -> Optional[timedelta]: """ the "eta" as datetime.timedelta. If downloading, estimated the ``timedelta`` left until the torrent is done. If seeding, estimated the ``timedelta`` left until seed ratio is reached. raw `eta` maybe negative: - `-1` for ETA Not Available. - `-2` for ETA Unknown. https://github.com/transmission/transmission/blob/3.00/libtransmission/transmission.h#L1748-L1749 """ eta = self.fields["eta"] if eta >= 0: return timedelta(seconds=eta) return None @property def eta_idle(self) -> Optional[timedelta]: v = self.fields["etaIdle"] if v >= 0: return timedelta(seconds=v) return None @property def file_count(self) -> Optional[int]: return self.fields["file-count"] def get_files(self) -> List[File]: """ Get list of files for this torrent. Note ---- The order of the files is guaranteed. The index of file object is the id of the file when calling :py:meth:`transmission_rpc.client.Client.set_files`. .. code-block:: python from transmission_rpc import Client torrent = Client().get_torrent(0) for file in enumerate(torrent.get_files()): print(file.id) """ result: List[File] = [] if "files" in self.fields: files = self.fields["files"] indices = range(len(files)) priorities = self.fields["priorities"] wanted = self.fields["wanted"] result.extend( File( selected=bool(raw_selected), priority=Priority(raw_priority), size=file["length"], name=file["name"], completed=file["bytesCompleted"], id=id, ) for id, file, raw_priority, raw_selected in zip(indices, files, priorities, wanted) ) return result @property def file_stats(self) -> List[FileStat]: return [FileStat(fields=x) for x in self.fields["fileStats"]] @property def group(self) -> str: return self.get("group", "") @property def have_unchecked(self) -> int: """ Byte count of all the partial piece data we have for this torrent. As pieces become complete, this value may decrease as portions of it are moved to "corrupt" or "haveValid". """ return self.fields["haveUnchecked"] @property def have_valid(self) -> int: """Byte count of all the checksum-verified data we have for this torrent.""" return self.fields["haveValid"] @property def honors_session_limits(self) -> bool: """true if session upload limits are honored""" return self.fields["honorsSessionLimits"] @property def is_finished(self) -> bool: return self.fields["isFinished"] @property def is_private(self) -> bool: return self.fields["isPrivate"] @property def is_stalled(self) -> bool: return self.fields["isStalled"] @property def labels(self) -> List[str]: return self.fields["labels"] @property def left_until_done(self) -> int: """ Byte count of how much data is left to be downloaded until we've got all the pieces that we want. [0...tr_stat.sizeWhenDone] """ return self.fields["leftUntilDone"] @property def magnet_link(self) -> str: return self.fields["magnetLink"] @property def manual_announce_time(self) -> datetime: return datetime.fromtimestamp(self.fields["manualAnnounceTime"], timezone.utc) @property def max_connected_peers(self) -> int: return self.fields["maxConnectedPeers"] @property def metadata_percent_complete(self) -> float: """ How much of the metadata the torrent has. For torrents added from a torrent this will always be 1. For magnet links, this number will from from 0 to 1 as the metadata is downloaded. Range is [0..1] """ return self.fields["metadataPercentComplete"] @property def peer_limit(self) -> int: """maximum number of peers""" return self.fields["peer-limit"] @property def peers(self) -> int: return self.fields["peers"] @property def peers_connected(self) -> int: """Number of peers that we're connected to""" return self.fields["peersConnected"] @property def peers_from(self) -> int: """How many peers we found out about from the tracker, or from pex, or from incoming connections, or from our resume file.""" return self.fields["peersFrom"] @property def peers_getting_from_us(self) -> int: """Number of peers that we're sending data to""" return self.fields["peersGettingFromUs"] @property def peers_sending_to_us(self) -> int: """Number of peers that are sending data to us.""" return self.fields["peersSendingToUs"] @property def percent_complete(self) -> float: """How much has been downloaded of the entire torrent. Range is [0..1]""" return self.fields["percentComplete"] @property def percent_done(self) -> float: """ How much has been downloaded of the files the user wants. This differs from percentComplete if the user wants only some of the torrent's files. Range is [0..1] """ return self.fields["percentDone"] @property def pieces(self) -> str: """ A bitfield holding pieceCount flags which are set to 'true' if we have the piece matching that position. JSON doesn't allow raw binary data, so this is a base64-encoded string. (Source: tr_torrent) """ return self.fields["pieces"] @property def piece_count(self) -> int: return self.fields["pieceCount"] @property def piece_size(self) -> int: return self.fields["pieceSize"] # TODO # @property # def priorities(self): # return self.fields["priorities"] @property def primary_mime_type(self) -> str: return self.fields["primary-mime-type"] @property def queue_position(self) -> int: """position of this torrent in its queue [0...n)""" return self.fields["queuePosition"] @property def rate_download(self) -> int: """download rate (B/s)""" return self.fields["rateDownload"] @property def rate_upload(self) -> int: """upload rate (B/s)""" return self.fields["rateUpload"] @property def recheck_progress(self) -> float: return self.fields["recheckProgress"] @property def seconds_downloading(self) -> int: return self.fields["secondsDownloading"] @property def seconds_seeding(self) -> int: return self.fields["secondsSeeding"] @property def seed_idle_limit(self) -> int: return self.fields["seedIdleLimit"] # @property # def seed_idle_mode(self) -> int: # """ which seeding inactivity to use. See tr_idlelimit""" # return self.fields["seedIdleMode"] @property def size_when_done(self) -> int: return self.fields["sizeWhenDone"] @property def trackers(self) -> List[Tracker]: return [Tracker(fields=x) for x in self.fields["trackers"]] @property def tracker_list(self) -> List[str]: """list of str of announce URLs""" return [x for x in self.fields["trackerList"].splitlines() if x] @property def tracker_stats(self) -> List[TrackerStats]: return [TrackerStats(fields=x) for x in self.fields["trackerStats"]] @property def total_size(self) -> int: return self.fields["totalSize"] @property def torrent_file(self) -> str: """ torrent file location on transmission server Examples -------- /var/lib/transmission-daemon/.config/transmission-daemon/torrents/00000000000000000000000000.torrent """ return self.fields["torrentFile"] @property def uploaded_ever(self) -> int: return self.fields["uploadedEver"] @property def upload_limit(self) -> int: return self.fields["uploadLimit"] @property def upload_limited(self) -> bool: return self.fields["uploadLimited"] @property def upload_ratio(self) -> float: return self.fields["uploadRatio"] @property def wanted(self) -> List: # TODO return self.fields["wanted"] @property def webseeds(self) -> List[str]: return self.fields["webseeds"] @property def webseeds_sending_to_us(self) -> int: """Number of webseeds that are sending data to us.""" return self.fields["webseedsSendingToUs"] @property def _status(self) -> int: """Get the torrent status""" return self.fields["status"] @property def _status_str(self) -> str: """Get the torrent status""" return get_status(self.fields["status"]) @property def status(self) -> Status: """ Returns the torrent status. Is either one of 'check pending', 'checking', 'downloading', 'download pending', 'seeding', 'seed pending' or 'stopped'. The first two is related to verification. Examples: .. code-block:: python torrent = Torrent() torrent.status.downloading torrent.status == 'downloading' """ return Status(self._status_str) @property def stopped(self) -> bool: return self._status == 0 @property def check_pending(self) -> bool: return self._status == 1 @property def checking(self) -> bool: return self._status == 2 @property def download_pending(self) -> bool: return self._status == 3 @property def downloading(self) -> bool: return self._status == 4 @property def seed_pending(self) -> bool: return self._status == 5 @property def seeding(self) -> bool: return self._status == 6 @property def progress(self) -> float: """ download progress in percent. """ try: # https://gist.github.com/jackiekazil/6201722#gistcomment-2788556 return round((100.0 * self.fields["percentDone"]), 2) except KeyError: try: size = self.fields["sizeWhenDone"] left = self.fields["leftUntilDone"] return round((100.0 * (size - left) / float(size)), 2) except ZeroDivisionError: return 0.0 @property def ratio(self) -> float: """ upload/download ratio. """ return float(self.fields["uploadRatio"]) @property def activity_date(self) -> datetime: """ The last time we uploaded or downloaded piece data on this torrent. the attribute ``activityDate`` as ``datetime.datetime`` in **UTC timezone**. .. note:: raw ``activityDate`` value could be ``0`` for never activated torrent, therefore it can't always be converted to local timezone. """ return datetime.fromtimestamp(self.fields["activityDate"], timezone.utc) @property def added_date(self) -> datetime: """When the torrent was first added.""" return datetime.fromtimestamp(self.fields["addedDate"], timezone.utc) @property def start_date(self) -> datetime: """raw field ``startDate`` as ``datetime.datetime`` in **utc timezone**.""" return datetime.fromtimestamp(self.fields["startDate"], timezone.utc) @property def done_date(self) -> Optional[datetime]: """the attribute "doneDate" as datetime.datetime. returns None if "doneDate" is invalid.""" done_date = self.fields["doneDate"] # Transmission might forget to set doneDate which is initialized to zero, # so if doneDate is zero return None if done_date == 0: return None return datetime.fromtimestamp(done_date).astimezone() def format_eta(self) -> str: """ Returns the attribute *eta* formatted as a string. * If eta is -1 the result is 'not available' * If eta is -2 the result is 'unknown' * Otherwise eta is formatted as ::. """ eta = self.fields["eta"] if eta == -1: return "not available" if eta == -2: return "unknown" return format_timedelta(timedelta(seconds=eta)) # @property # def download_limit(self) -> Optional[int]: # """The download limit. # # Can be a number or None. # """ # if self.fields["downloadLimited"]: # return self.fields["downloadLimit"] # return None @property def priority(self) -> Priority: """ Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator. """ return Priority(self.fields["bandwidthPriority"]) @property def seed_idle_mode(self) -> IdleMode: """ Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'. * global, use session seed idle limit. * single, use torrent seed idle limit. See seed_idle_limit. * unlimited, no seed idle limit. """ return IdleMode(self.fields["seedIdleMode"]) @property def seed_ratio_limit(self) -> float: """ Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator. """ return float(self.fields["seedRatioLimit"]) @property def seed_ratio_mode(self) -> RatioLimitMode: """ Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'. * global, use session seed ratio limit. * single, use torrent seed ratio limit. See seed_ratio_limit. * unlimited, no seed ratio limit. """ return RatioLimitMode(self.fields["seedRatioMode"]) def __repr__(self) -> str: return f'' def __str__(self) -> str: return f'Torrent "{self.name}"' transmission-rpc-7.0.3/transmission_rpc/types.py000066400000000000000000000027141451315226700221610ustar00rootroot00000000000000from typing import Any, Dict, Tuple, Union, TypeVar, Optional, NamedTuple from transmission_rpc.constants import Priority _Number = Union[int, float] _Timeout = Optional[Union[_Number, Tuple[_Number, _Number]]] T = TypeVar("T") class Container: fields: Dict[str, Any] #: raw fields def __init__(self, *, fields: Dict[str, Any]): self.fields = fields def get(self, key: str, default: Optional[T] = None) -> Any: """get the raw value by the **raw rpc response key**""" return self.fields.get(key, default) class File(NamedTuple): name: str # file name size: int # file size in bytes completed: int # bytes completed priority: Priority selected: bool # if selected for download id: int # id of the file of this torrent, not should not be used outside the torrent scope. class Group(Container): @property def name(self) -> str: return self.fields["name"] @property def honors_session_limits(self) -> bool: return self.fields["honorsSessionLimits"] @property def speed_limit_down_enabled(self) -> bool: return self.fields["speed-limit-down-enabled"] @property def speed_limit_down(self) -> int: return self.fields["speed-limit-down"] @property def speed_limit_up_enabled(self) -> bool: return self.fields["speed-limit-up-enabled"] @property def speed_limit_up(self) -> int: return self.fields["speed-limit-up"] transmission-rpc-7.0.3/transmission_rpc/utils.py000066400000000000000000000051551451315226700221570ustar00rootroot00000000000000# Copyright (c) 2018-2021 Trim21 # Copyright (c) 2008-2014 Erik Svensson # Licensed under the MIT license. import base64 import pathlib import datetime from typing import List, Tuple, Union, BinaryIO, Optional from urllib.parse import urlparse from transmission_rpc import constants UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"] def format_size(size: int) -> Tuple[float, str]: """ Format byte size into IEC prefixes, B, KiB, MiB ... """ s = float(size) i = 0 while s >= 1024.0 and i < len(UNITS): i += 1 s /= 1024.0 return s, UNITS[i] def format_speed(size: int) -> Tuple[float, str]: """ Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ... """ (s, unit) = format_size(size) return s, f"{unit}/s" def format_timedelta(delta: datetime.timedelta) -> str: """ Format datetime.timedelta into ::. """ minutes, seconds = divmod(delta.seconds, 60) hours, minutes = divmod(minutes, 60) return f"{delta.days:d} {hours:02d}:{minutes:02d}:{seconds:02d}" def get_torrent_arguments(rpc_version: int) -> List[str]: """ Get torrent arguments for method in specified Transmission RPC version. """ accessible = [] for argument, info in constants.TORRENT_GET_ARGS.items(): valid_version = True if rpc_version < info.added_version: valid_version = False if info.removed_version is not None and info.removed_version <= rpc_version: valid_version = False if valid_version: accessible.append(argument) return accessible def _try_read_torrent(torrent: Union[BinaryIO, str, bytes, pathlib.Path]) -> Optional[str]: # pylint: disable=R0911 """ if torrent should be encoded with base64, return a non-None value. """ # torrent is a str, may be a url if isinstance(torrent, str): parsed_uri = urlparse(torrent) # torrent starts with file, read from local disk and encode it to base64 url. if parsed_uri.scheme in ["https", "http", "magnet"]: return None if parsed_uri.scheme in ["file"]: raise ValueError("support for `file://` URL has been removed.") elif isinstance(torrent, pathlib.Path): return base64.b64encode(torrent.read_bytes()).decode("utf-8") elif isinstance(torrent, bytes): return base64.b64encode(torrent).decode("utf-8") # maybe a file, try read content and encode it. elif hasattr(torrent, "read"): return base64.b64encode(torrent.read()).decode("utf-8") return None