pax_global_header00006660000000000000000000000064150006324400014504gustar00rootroot0000000000000052 comment=46d3b72449718391c74de2038e91b4c5e517ea5b anytree-2.13.0/000077500000000000000000000000001500063244000132365ustar00rootroot00000000000000anytree-2.13.0/.codespellignore000066400000000000000000000000171500063244000164130ustar00rootroot00000000000000udo anchestors anytree-2.13.0/.cookiecutter.json000066400000000000000000000006171500063244000167130ustar00rootroot00000000000000{ "cookiecutter": { "_checkout": null, "_output_dir": "/Users/c0fec0de/projects", "_repo_dir": "/Users/c0fec0de/.cookiecutters/python-template", "_template": "gh:nbiotcloud/python-template", "name": "anytree", "snake_name": "anytree", "title": "Powerful and Lightweight Python Tree Data Structure with various plugins", "user": "c0fec0de", "year": "2016" } } anytree-2.13.0/.github/000077500000000000000000000000001500063244000145765ustar00rootroot00000000000000anytree-2.13.0/.github/workflows/000077500000000000000000000000001500063244000166335ustar00rootroot00000000000000anytree-2.13.0/.github/workflows/main.yml000066400000000000000000000036731500063244000203130ustar00rootroot00000000000000name: main on: [push, pull_request, release] jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: - "ubuntu-latest" - "macos-latest" # - "windows-latest" python-version: - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: astral-sh/setup-uv@v5 with: version: "0.6.9" python-version: ${{ matrix.python-version }} enable-cache: true cache-dependency-glob: "uv.lock" - name: Install graphviz run: sudo apt install -y graphviz || true - name: Run all run: make all - name: Upload coverage data to coveralls.io run: uv run --frozen -- coveralls --service=github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_FLAG_NAME: ${{ matrix.test-name }} COVERALLS_PARALLEL: true coveralls: name: Indicate completion to coveralls.io needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: astral-sh/setup-uv@v5 with: version: "0.6.9" python-version: ${{ matrix.python-version }} enable-cache: true cache-dependency-glob: "uv.lock" - name: Upload coverage data to coveralls.io run: uv run --frozen -- coveralls --service=github --finish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish: if: github.event_name == 'push' && github.ref_type == 'tag' needs: coveralls name: upload release to PyPI runs-on: ubuntu-latest permissions: id-token: write steps: - uses: actions/checkout@v4 - uses: pdm-project/setup-pdm@v4 - name: Publish package distributions to PyPI run: pdm publish anytree-2.13.0/.gitignore000066400000000000000000000003261500063244000152270ustar00rootroot00000000000000__pycache__ .*_cache/ .coverage .coverage* .DS_Store .hypothesis .mypy_cache .nox/ .pdm-python .test2ref .tox .venv* *.egg-info/ *.pyc *.xml /.vscode build/ dist/ docs/_readthedocs/ poetry.lock report.xml setup.py anytree-2.13.0/.pre-commit-config.yaml000066400000000000000000000022771500063244000175270ustar00rootroot00000000000000exclude: ^tests/refdata/.* repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. rev: v0.11.2 hooks: # Run the linter. - id: ruff args: [--fix] # Run the formatter. - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-toml - id: check-json - id: check-xml - id: debug-statements - id: check-added-large-files - id: check-merge-conflict - id: mixed-line-ending - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.5.5 hooks: - id: remove-tabs args: [--whitespaces-count, "2"] # defaults to: 4 # - id: insert-license # files: \.py$ # args: # - --license-filepath # - LICENSE # - --use-current-year - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: - id: codespell args: - "--write-changes" - "--ignore-words=.codespellignore" additional_dependencies: - tomli exclude: '^uv\.lock$' anytree-2.13.0/.readthedocs.yaml000066400000000000000000000006441500063244000164710ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" commands: - pip install --upgrade --no-cache-dir pip - pip install --no-cache-dir uv - make doc - mkdir _readthedocs - cp -r docs/build _readthedocs anytree-2.13.0/CONTRIBUTING.md000066400000000000000000000044521500063244000154740ustar00rootroot00000000000000# CONTRIBUTING Please follow github workflow. Create a ticket and/or branch. Create a pull-request. ## Local Development ### Installation Please install these tools: * [`uv` Installation](https://docs.astral.sh/uv/getting-started/installation/) * [`make`](https://www.gnu.org/software/make/) * [`git`](https://git-scm.com/) * [Visual Studio Code](https://code.visualstudio.com/) * [`graphviz`](https://graphviz.org/) ### Editor Start Visual Studio Code: ```bash make code ``` ### Testing Run auto-formatting, linting, tests and documentation build: ```bash make all ``` See `make help` for any further details. Please note that `tests/refdata` contains reference data from test runs. `make test2refdata` updates this directory. ## Project Structure The project contains these files and directories: | File/Directory | Description | |---|---| | `src/` | Python Package Sources - the files this is all about | | `pyproject.toml` | Python Package Meta File. Also contains all tool settings | | `.gitignore` | Lists of files and directories ignored by version control system | | `.github/` | Github Settings | | `.readthedocs.yaml` | Documentation Server Configuration | | `.pre-commit-config.yaml` | Pre-Commit Check Configuration | | `uv.lock` | File with resolved python package dependencies | Next to that, there are some temporary files ignored by version control system. | File/Directory | Description | |---|---| | `htmlcov/` | Test Execution Code Coverage Report in HTML format | | `report.xml` | Test Execution Report | | `.venv` | Virtual Environments | ## Branches * `2.x.x` main line for `2.x.x` * `3.x.x` main line for `3.x.x` * documentation links refer to `3.x.x` * 2.x.x can be merged to 3.x.x * `main` * 2.x.x can be merged to main ### Release ```bash # Update 3.x.x git checkout 3.x.x git pull origin 2.x.x git push origin 3.x.x prev_version=$(poetry version -s) # Version Bump poetry version minor # OR poetry version patch # Commit, Tag and Push version=$(poetry version -s) sed "s/$prev_version/$version/g" -i anytree/__init__.py git commit -m"version bump to ${version}" pyproject.toml anytree/__init__.py git tag "${version}" -m "Release ${version}" git push git push --tags # Update main git checkout main git pull origin 2.x.x git push origin main # Publishing is handled by CI ``` anytree-2.13.0/LICENSE000066400000000000000000000261351500063244000142520ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. anytree-2.13.0/Makefile000066400000000000000000000036561500063244000147100ustar00rootroot00000000000000_GREEN:=\033[0;32m _BLUE:=\033[0;34m _BOLD:=\033[1m _NORM:=\033[0m ENV:=uv run --frozen -- PYTEST_OPTIONS= .PHONY: help help: ## [DEFAULT] Show this help @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?##"}; {printf "${_BLUE}${_BOLD}%-10s${_NORM} %s\n", $$1, $$2}' .PHONY: all all: ## Do everything taggged with [ALL] below @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep "\[ALL\]" | grep -v "^all" | awk 'BEGIN {FS = ":.*?##"}; {printf "%s ", $$1}' | xargs make @echo "\n ${_GREEN}${_BOLD}PASS${_NORM}\n" .PHONY: pre-commit pre-commit: .venv/.valid .git/hooks/pre-commit ## [ALL] Run 'pre-commit' on all files ${ENV} pre-commit run --all-files .git/hooks/pre-commit: .venv/.valid ${ENV} pre-commit install --install-hooks .PHONY: test test: .venv/.valid ## [ALL] Run Unittests via 'pytest' with {PYTEST_OPTIONS} ${ENV} pytest -vv ${PYTEST_OPTIONS} @echo "See coverage report:\n\n file://${PWD}/htmlcov/index.html\n" .PHONY: test2refdata test2refdata: .venv/.valid ## Run Unittests via 'pytest' with {PYTEST_OPTIONS} and update tests/refdata rm -rf tests/refdata touch .test2ref ${ENV} pytest -vv ${PYTEST_OPTIONS} @echo "See coverage report:\n\n file://${PWD}/htmlcov/index.html\n" rm .test2ref .PHONY: checktypes checktypes: .venv/.valid ## [ALL] Run Type-Checking via 'mypy' ${ENV} mypy src .PHONY: doc doc: .venv/.valid ## [ALL] Build Documentation via 'mkdocs' ${ENV} make html -C docs .PHONY: code code: ## Start Visual Studio Code code anytree.code-workspace & .PHONY: clean clean: ## Remove everything mentioned by '.gitignore' file git clean -Xdf .PHONY: distclean distclean: ## Remove everything mentioned by '.gitignore' file and UNTRACKED files git clean -xdf .PHONY: shell shell: ## Open a project specific SHELL. For leaving use 'exit'. ${ENV} ${SHELL} # Helper .venv/.valid: pyproject.toml uv.lock uv sync --frozen @touch $@ uv.lock: uv lock anytree-2.13.0/README.rst000066400000000000000000000157001500063244000147300ustar00rootroot00000000000000.. image:: https://badge.fury.io/py/anytree.svg :target: https://badge.fury.io/py/anytree .. image:: https://img.shields.io/pypi/dm/anytree.svg?label=pypi%20downloads :target: https://pypi.python.org/pypi/anytree .. image:: https://readthedocs.org/projects/anytree/badge/?version=latest :target: https://anytree.readthedocs.io/en/latest/?badge=latest .. image:: https://coveralls.io/repos/github/c0fec0de/anytree/badge.svg :target: https://coveralls.io/github/c0fec0de/anytree .. image:: https://api.codeclimate.com/v1/badges/e6d325d6fd23a93aab20/maintainability :target: https://codeclimate.com/github/c0fec0de/anytree/maintainability :alt: Maintainability .. image:: https://img.shields.io/pypi/pyversions/anytree.svg :target: https://pypi.python.org/pypi/anytree .. image:: https://img.shields.io/badge/code%20style-pep8-brightgreen.svg :target: https://www.python.org/dev/peps/pep-0008/ .. image:: https://img.shields.io/badge/code%20style-pep257-brightgreen.svg :target: https://www.python.org/dev/peps/pep-0257/ .. image:: https://img.shields.io/badge/linter-pylint-%231674b1?style=flat :target: https://www.pylint.org/ .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black .. image:: https://img.shields.io/github/contributors/c0fec0de/anytree.svg :target: https://github.com/c0fec0de/anytree/graphs/contributors/ .. image:: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square :target: http://makeapullrequest.com .. image:: https://img.shields.io/github/issues-pr/c0fec0de/anytree.svg :target: https://github.com/c0fec0de/anytree/pulls .. image:: https://img.shields.io/github/issues-pr-closed/c0fec0de/anytree.svg :target: https://github.com/c0fec0de/anytree/pulls?q=is%3Apr+is%3Aclosed Links ===== * Documentation_ * PyPI_ * GitHub_ * Changelog_ * Issues_ * Contributors_ * If you enjoy anytree_ .. image:: https://github.com/c0fec0de/anytree/raw/devel/docs/static/buymeacoffee.png :target: https://www.buymeacoffee.com/1oYX0sw .. _anytree: https://anytree.readthedocs.io/en/stable/ .. _Documentation: https://anytree.readthedocs.io/en/stable/ .. _PyPI: https://pypi.org/project/anytree .. _GitHub: https://github.com/c0fec0de/anytree .. _Changelog: https://github.com/c0fec0de/anytree/releases .. _Issues: https://github.com/c0fec0de/anytree/issues .. _Contributors: https://github.com/c0fec0de/anytree/graphs/contributors .. _Node: https://anytree.readthedocs.io/en/stable/api/anytree.node.html#anytree.node.node.Node .. _RenderTree: https://anytree.readthedocs.io/en/stable/api/anytree.render.html#anytree.render.RenderTree .. _UniqueDotExporter: https://anytree.readthedocs.io/en/stable/exporter/dotexporter.html#anytree.exporter.dotexporter.UniqueDotExporter .. _NodeMixin: https://anytree.readthedocs.io/en/stable/api/anytree.node.html#anytree.node.nodemixin.NodeMixin .. _Importers: https://anytree.readthedocs.io/en/stable/importer.html .. _Exporters: https://anytree.readthedocs.io/en/stable/exporter.html Getting started --------------- .. _getting_started: Usage is simple. **Construction** >>> from anytree import Node, RenderTree >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> dan = Node("Dan", parent=udo) >>> jet = Node("Jet", parent=dan) >>> jan = Node("Jan", parent=dan) >>> joe = Node("Joe", parent=dan) **Node** >>> print(udo) Node('/Udo') >>> print(joe) Node('/Udo/Dan/Joe') **Tree** >>> for pre, fill, node in RenderTree(udo): ... print("%s%s" % (pre, node.name)) Udo ├── Marc │ └── Lian └── Dan ├── Jet ├── Jan └── Joe For details see Node_ and RenderTree_. **Visualization** >>> from anytree.exporter import UniqueDotExporter >>> # graphviz needs to be installed for the next line! >>> UniqueDotExporter(udo).to_picture("udo.png") .. image:: https://anytree.readthedocs.io/en/latest/_images/udo.png The UniqueDotExporter_ can be started at any node and has various formatting hookups: >>> UniqueDotExporter(dan, ... nodeattrfunc=lambda node: "fixedsize=true, width=1, height=1, shape=diamond", ... edgeattrfunc=lambda parent, child: "style=bold" ... ).to_picture("dan.png") .. image:: https://anytree.readthedocs.io/en/latest/_images/dan.png There are various other Importers_ and Exporters_. **Manipulation** A second tree: >>> mary = Node("Mary") >>> urs = Node("Urs", parent=mary) >>> chris = Node("Chris", parent=mary) >>> marta = Node("Marta", parent=mary) >>> print(RenderTree(mary)) Node('/Mary') ├── Node('/Mary/Urs') ├── Node('/Mary/Chris') └── Node('/Mary/Marta') Append: >>> udo.parent = mary >>> print(RenderTree(mary)) Node('/Mary') ├── Node('/Mary/Urs') ├── Node('/Mary/Chris') ├── Node('/Mary/Marta') └── Node('/Mary/Udo') ├── Node('/Mary/Udo/Marc') │ └── Node('/Mary/Udo/Marc/Lian') └── Node('/Mary/Udo/Dan') ├── Node('/Mary/Udo/Dan/Jet') ├── Node('/Mary/Udo/Dan/Jan') └── Node('/Mary/Udo/Dan/Joe') Subtree rendering: >>> print(RenderTree(marc)) Node('/Mary/Udo/Marc') └── Node('/Mary/Udo/Marc/Lian') Cut: >>> dan.parent = None >>> print(RenderTree(dan)) Node('/Dan') ├── Node('/Dan/Jet') ├── Node('/Dan/Jan') └── Node('/Dan/Joe') **Extending any python class to become a tree node** The entire tree magic is encapsulated by NodeMixin_ add it as base class and the class becomes a tree node: >>> from anytree import NodeMixin, RenderTree >>> class MyBaseClass(object): # Just an example of a base class ... foo = 4 >>> class MyClass(MyBaseClass, NodeMixin): # Add Node feature ... def __init__(self, name, length, width, parent=None, children=None): ... super(MyClass, self).__init__() ... self.name = name ... self.length = length ... self.width = width ... self.parent = parent ... if children: ... self.children = children Just set the `parent` attribute to reflect the tree relation: >>> my0 = MyClass('my0', 0, 0) >>> my1 = MyClass('my1', 1, 0, parent=my0) >>> my2 = MyClass('my2', 0, 2, parent=my0) >>> for pre, fill, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 The `children` can be used likewise: >>> my0 = MyClass('my0', 0, 0, children=[ ... MyClass('my1', 1, 0), ... MyClass('my2', 0, 2), ... ]) >>> for pre, fill, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 Documentation ------------- Please see the Documentation_ for all details. Installation ------------ To install the `anytree` module run:: pip install anytree If you do not have write-permissions to the python installation, try:: pip install anytree --user anytree-2.13.0/anytree.code-workspace000066400000000000000000000005641500063244000175420ustar00rootroot00000000000000{ "folders": [ { "path": "." } ], "settings": { "python.defaultInterpreterPath": ".venv/bin/python3", "python.terminal.activateEnvInCurrentTerminal": true, "files.trimTrailingWhitespace": true }, "extensions": { "recommendations": [ "ms-python.python", "ms-python.vscode-pylance", "charliermarsh.ruff" ] } } anytree-2.13.0/conftest.py000066400000000000000000000012301500063244000154310ustar00rootroot00000000000000import pytest # https://stackoverflow.com/questions/46962007/how-to-automatically-change-to-pytest-temporary-directory-for-all-doctests @pytest.fixture(autouse=True) def _docdir(request): # Trigger ONLY for the doctests. doctest_plugin = request.config.pluginmanager.getplugin("doctest") if isinstance(request.node, doctest_plugin.DoctestItem): # Get the fixture dynamically by its name. tmpdir = request.getfixturevalue("tmpdir") # Chdir only for the duration of the test. with tmpdir.as_cwd(): yield else: # For normal tests, we have to yield, since this is a yield-fixture. yield anytree-2.13.0/docs/000077500000000000000000000000001500063244000141665ustar00rootroot00000000000000anytree-2.13.0/docs/Makefile000066400000000000000000000011711500063244000156260ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) anytree-2.13.0/docs/api.rst000066400000000000000000000003471500063244000154750ustar00rootroot00000000000000API === .. toctree:: :maxdepth: 1 api/anytree.node api/anytree.iterators api/anytree.render api/anytree.search api/anytree.cachedsearch api/anytree.resolver api/anytree.walker api/anytree.util anytree-2.13.0/docs/api/000077500000000000000000000000001500063244000147375ustar00rootroot00000000000000anytree-2.13.0/docs/api/anytree.cachedsearch.rst000066400000000000000000000001101500063244000215240ustar00rootroot00000000000000Cached Searching ================ .. automodule:: anytree.cachedsearch anytree-2.13.0/docs/api/anytree.iterators.rst000066400000000000000000000004761500063244000211620ustar00rootroot00000000000000Tree Iteration ============== .. automodule:: anytree.iterators .. automodule:: anytree.iterators.preorderiter .. automodule:: anytree.iterators.postorderiter .. automodule:: anytree.iterators.levelorderiter .. automodule:: anytree.iterators.levelordergroupiter .. automodule:: anytree.iterators.zigzaggroupiter anytree-2.13.0/docs/api/anytree.node.rst000066400000000000000000000006041500063244000200640ustar00rootroot00000000000000Node Classes ============ .. automodule:: anytree.node .. automodule:: anytree.node.anynode .. automodule:: anytree.node.node .. automodule:: anytree.node.nodemixin :private-members: .. automodule:: anytree.node.lightnodemixin :private-members: .. automodule:: anytree.node.symlinknode .. automodule:: anytree.node.symlinknodemixin .. automodule:: anytree.node.exceptions anytree-2.13.0/docs/api/anytree.render.rst000066400000000000000000000000761500063244000204210ustar00rootroot00000000000000Tree Rendering ============== .. automodule:: anytree.render anytree-2.13.0/docs/api/anytree.resolver.rst000066400000000000000000000001021500063244000207710ustar00rootroot00000000000000Node Resolution =============== .. automodule:: anytree.resolver anytree-2.13.0/docs/api/anytree.search.rst000066400000000000000000000000641500063244000204040ustar00rootroot00000000000000Searching ========= .. automodule:: anytree.search anytree-2.13.0/docs/api/anytree.util.rst000066400000000000000000000000621500063244000201120ustar00rootroot00000000000000Utilities ========= .. automodule:: anytree.util anytree-2.13.0/docs/api/anytree.walker.rst000066400000000000000000000000721500063244000204230ustar00rootroot00000000000000Tree Walking ============ .. automodule:: anytree.walker anytree-2.13.0/docs/conf.py000066400000000000000000000025461500063244000154740ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "anytree" copyright = "2016-2023, c0fec0de" author = "c0fec0de" description = "Python Tree Data" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.viewcode", "sphinx.ext.napoleon", "sphinx.ext.intersphinx", ] templates_path = ["_templates"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] source_suffix = ".rst" # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinxdoc" html_static_path = ["static"] intersphinx_mapping = { "python": ("http://docs.python.org/3", None), } intersphinx_cache_limit = 1 autodoc_default_options = { "members": None, # Include all members (methods). "member-order": "bysource", } autoclass_content = "both" anytree-2.13.0/docs/dotexport.rst000066400000000000000000000014271500063244000167540ustar00rootroot00000000000000Export to DOT ============= Any :any:`anytree` graph can be converted to a graphviz_ graph. This tree:: >>> from anytree import Node >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> s1a = Node("sub1A", parent=s1) >>> s1b = Node("sub1B", parent=s1) >>> s1c = Node("sub1C", parent=s1) >>> s1ca = Node("sub1Ca", parent=s1c) Can be rendered to a tree by :any:`RenderTreeGraph`:: >>> from anytree.dotexport import RenderTreeGraph >>> RenderTreeGraph(root).to_picture("tree.png") # doctest: +SKIP .. image:: static/tree.png .. automodule:: anytree.dotexport :inherited-members: .. _graphviz: http://www.graphviz.org/ anytree-2.13.0/docs/exporter.rst000066400000000000000000000006721500063244000165750ustar00rootroot00000000000000Exporter ======== One fundamental idea behind *anytree* is the common tree node data structure, which can be imported from different formats and exported to different formats. Available exporters: .. toctree:: :maxdepth: 1 exporter/dictexporter exporter/jsonexporter exporter/dotexporter exporter/mermaidexporter Exporter missing? File a request here: Issues_. .. _Issues: https://github.com/c0fec0de/anytree/issues anytree-2.13.0/docs/exporter/000077500000000000000000000000001500063244000160365ustar00rootroot00000000000000anytree-2.13.0/docs/exporter/dictexporter.rst000066400000000000000000000001271500063244000213040ustar00rootroot00000000000000Dictionary Exporter =================== .. automodule:: anytree.exporter.dictexporter anytree-2.13.0/docs/exporter/dotexporter.rst000066400000000000000000000002451500063244000211500ustar00rootroot00000000000000Dot Exporter ============ For any details about the `dot` language, see graphviz_ .. automodule:: anytree.exporter.dotexporter .. _graphviz: https://graphviz.org anytree-2.13.0/docs/exporter/jsonexporter.rst000066400000000000000000000001131500063244000213250ustar00rootroot00000000000000JSON Exporter ============= .. automodule:: anytree.exporter.jsonexporter anytree-2.13.0/docs/exporter/mermaidexporter.rst000066400000000000000000000003131500063244000217740ustar00rootroot00000000000000Mermaid Exporter ================ For any details about the `Mermaid` language, see mermaid_ .. automodule:: anytree.exporter.mermaidexporter .. _mermaid: https://mermaid.js.org/syntax/flowchart.html anytree-2.13.0/docs/importer.rst000066400000000000000000000006041500063244000165610ustar00rootroot00000000000000Importer ======== One fundamental idea behind *anytree* is the common tree node data structure, which can be imported from different formats and exported to different formats. Available importers: .. toctree:: :maxdepth: 1 importer/dictimporter importer/jsonimporter Importer missing? File a request here: Issues_. .. _Issues: https://github.com/c0fec0de/anytree/issues anytree-2.13.0/docs/importer/000077500000000000000000000000001500063244000160275ustar00rootroot00000000000000anytree-2.13.0/docs/importer/dictimporter.rst000066400000000000000000000001271500063244000212660ustar00rootroot00000000000000Dictionary Importer =================== .. automodule:: anytree.importer.dictimporter anytree-2.13.0/docs/importer/jsonimporter.rst000066400000000000000000000001131500063244000213070ustar00rootroot00000000000000JSON Importer ============= .. automodule:: anytree.importer.jsonimporter anytree-2.13.0/docs/index.rst000066400000000000000000000144071500063244000160350ustar00rootroot00000000000000******************** Any Python Tree Data ******************** .. image:: https://badge.fury.io/py/anytree.svg :target: https://badge.fury.io/py/anytree .. image:: https://img.shields.io/pypi/dm/anytree.svg?label=pypi%20downloads :target: https://pypi.python.org/pypi/anytree .. image:: https://readthedocs.org/projects/anytree/badge/?version=latest :target: https://anytree.readthedocs.io/en/latest/?badge=latest .. image:: https://coveralls.io/repos/github/c0fec0de/anytree/badge.svg :target: https://coveralls.io/github/c0fec0de/anytree .. image:: https://readthedocs.org/projects/anytree/badge/?version=stable :target: https://anytree.readthedocs.io/en/stable/ .. image:: https://img.shields.io/pypi/pyversions/anytree.svg :target: https://pypi.python.org/pypi/anytree .. image:: https://img.shields.io/github/contributors/c0fec0de/anytree.svg :target: https://github.com/c0fec0de/anytree/graphs/contributors/ .. image:: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square :target: http://makeapullrequest.com .. image:: https://img.shields.io/github/issues-pr/c0fec0de/anytree.svg :target: https://github.com/c0fec0de/anytree/pulls .. image:: https://img.shields.io/github/issues-pr-closed/c0fec0de/anytree.svg :target: https://github.com/c0fec0de/anytree/pulls?q=is%3Apr+is%3Aclosed Simple, lightweight and extensible Tree_ data structure. Feel free to share_ info about your anytree project. .. _share: https://github.com/c0fec0de/anytree/issues/34 .. toctree:: :maxdepth: 2 installation intro api importer exporter tricks Links ===== * Documentation_ * GitHub_ * PyPI_ * Changelog_ * Issues_ * Contributors_ * If you enjoy anytree_ .. image:: https://cdn.buymeacoffee.com/buttons/default-orange.png :width: 150 :target: https://www.buymeacoffee.com/1oYX0sw Feel free to share_ info about your anytree project. .. _anytree: https://anytree.readthedocs.io/en/stable/ .. _Documentation: https://anytree.readthedocs.io/en/stable/ .. _GitHub: https://github.com/c0fec0de/anytree .. _PyPI: https://pypi.org/project/anytree/ .. _Changelog: https://github.com/c0fec0de/anytree/releases .. _Issues: https://github.com/c0fec0de/anytree/issues .. _Contributors: https://github.com/c0fec0de/anytree/graphs/contributors .. _share: https://github.com/c0fec0de/anytree/issues/34 .. _Tree: https://en.wikipedia.org/wiki/Tree_(data_structure) Getting started =============== .. _getting_started: Usage is simple. **Construction** >>> from anytree import Node, RenderTree >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> dan = Node("Dan", parent=udo) >>> jet = Node("Jet", parent=dan) >>> jan = Node("Jan", parent=dan) >>> joe = Node("Joe", parent=dan) **Node** >>> print(udo) Node('/Udo') >>> print(joe) Node('/Udo/Dan/Joe') **Tree** >>> for pre, fill, node in RenderTree(udo): ... print("%s%s" % (pre, node.name)) Udo ├── Marc │ └── Lian └── Dan ├── Jet ├── Jan └── Joe For details see :any:`Node` and :any:`RenderTree`. **Visualization** >>> from anytree.exporter import UniqueDotExporter >>> # graphviz needs to be installed for the next line! >>> UniqueDotExporter(udo).to_picture("udo.png") # doctest: +SKIP .. image:: static/udo.png The :any:`UniqueDotExporter` can be started at any node and has various formatting hookups: >>> UniqueDotExporter(dan, ... nodeattrfunc=lambda node: "fixedsize=true, width=1, height=1, shape=diamond", ... edgeattrfunc=lambda parent, child: "style=bold" ... ).to_picture("dan.png") # doctest: +SKIP .. image:: static/dan.png **Manipulation** A second tree: >>> mary = Node("Mary") >>> urs = Node("Urs", parent=mary) >>> chris = Node("Chris", parent=mary) >>> marta = Node("Marta", parent=mary) >>> print(RenderTree(mary)) Node('/Mary') ├── Node('/Mary/Urs') ├── Node('/Mary/Chris') └── Node('/Mary/Marta') Append: >>> udo.parent = mary >>> print(RenderTree(mary)) Node('/Mary') ├── Node('/Mary/Urs') ├── Node('/Mary/Chris') ├── Node('/Mary/Marta') └── Node('/Mary/Udo') ├── Node('/Mary/Udo/Marc') │ └── Node('/Mary/Udo/Marc/Lian') └── Node('/Mary/Udo/Dan') ├── Node('/Mary/Udo/Dan/Jet') ├── Node('/Mary/Udo/Dan/Jan') └── Node('/Mary/Udo/Dan/Joe') Subtree rendering: >>> print(RenderTree(marc)) Node('/Mary/Udo/Marc') └── Node('/Mary/Udo/Marc/Lian') Cut/Delete: >>> dan.parent = None >>> print(RenderTree(dan)) Node('/Dan') ├── Node('/Dan/Jet') ├── Node('/Dan/Jan') └── Node('/Dan/Joe') >>> print(RenderTree(mary)) Node('/Mary') ├── Node('/Mary/Urs') ├── Node('/Mary/Chris') ├── Node('/Mary/Marta') └── Node('/Mary/Udo') └── Node('/Mary/Udo/Marc') └── Node('/Mary/Udo/Marc/Lian') **Extending any python class to become a tree node** The entire tree magic is encapsulated by :any:`NodeMixin`, add it as base class and the class becomes a tree node: >>> from anytree import NodeMixin, RenderTree >>> class MyBaseClass(object): # Just an example of a base class ... foo = 4 >>> class MyClass(MyBaseClass, NodeMixin): # Add Node feature ... def __init__(self, name, length, width, parent=None, children=None): ... super(MyClass, self).__init__() ... self.name = name ... self.length = length ... self.width = width ... self.parent = parent ... if children: # set children only if given ... self.children = children Just set the `parent` attribute to reflect the tree relation: >>> my0 = MyClass('my0', 0, 0) >>> my1 = MyClass('my1', 1, 0, parent=my0) >>> my2 = MyClass('my2', 0, 2, parent=my0) >>> for pre, fill, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 The `children` can be used likewise: >>> my0 = MyClass('my0', 0, 0, children=[ ... MyClass('my1', 1, 0), ... MyClass('my2', 0, 2), ... ]) >>> for pre, fill, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 anytree-2.13.0/docs/installation.rst000066400000000000000000000004011500063244000174140ustar00rootroot00000000000000Installation ============ To install the `anytree` module run:: pip install anytree If you do not have write-permissions to the python installation, try:: pip install anytree --user .. _Tree: https://en.wikipedia.org/wiki/Tree_(data_structure) anytree-2.13.0/docs/intro.rst000066400000000000000000000120131500063244000160500ustar00rootroot00000000000000Introduction ============ Overview -------- `anytree` is split into the following parts: **Node Classes** * :any:`Node`: a simple tree node with at least a name attribute and any number of additional attributes. * :any:`AnyNode`: a generic tree node and any number of additional attributes. * :any:`NodeMixin`: extends any python class to a tree node. **Node Resolution** * :any:`Resolver`: retrieve node via absolute or relative path. * :any:`Walker`: walk from one node to an other. **Tree Iteration Strategies** * :any:`PreOrderIter`: iterate over tree using pre-order strategy * :any:`PostOrderIter`: iterate over tree using post-order strategy * :any:`LevelOrderIter`: iterate over tree using level-order strategy * :any:`LevelOrderGroupIter`: iterate over tree using level-order strategy returning group for every level * :any:`ZigZagGroupIter`: iterate over tree using level-order strategy returning group for every level **Tree Rendering** * :any:`RenderTree` using the following styles: * :any:`AsciiStyle` * :any:`ContStyle` * :any:`ContRoundStyle` * :any:`DoubleStyle` Basics ------ The only tree relevant information is the `parent` attribute. If `None` the node is root node. If set to another node, the node becomes the child of it. >>> from anytree import Node, RenderTree >>> udo = Node("Udo") >>> marc = Node("Marc") >>> lian = Node("Lian", parent=marc) >>> print(RenderTree(udo)) Node('/Udo') >>> print(RenderTree(marc)) Node('/Marc') └── Node('/Marc/Lian') Every node has a :any:`children` attribute with a tuple of all children: >>> udo.children () >>> marc.children (Node('/Marc/Lian'),) >>> lian.children () **Add: Single Node Attach** Just set the parent attribute and the node becomes a child node: >>> marc.parent = udo >>> print(RenderTree(udo)) Node('/Udo') └── Node('/Udo/Marc') └── Node('/Udo/Marc/Lian') **Delete: Single Node Detach** A node becomes a root node, if you set the parent attribute to `None`: >>> lian.is_root False >>> lian.parent = None >>> lian.is_root True The node is deleted from the tree: >>> print(RenderTree(udo)) Node('/Udo') └── Node('/Udo/Marc') **Modify Multiple Child Nodes** Assume the following tree: >>> n = Node("n") >>> a = Node("a", parent=n) >>> b = Node("b", parent=n) >>> c = Node("c", parent=n) >>> d = Node("d") >>> n.children (Node('/n/a'), Node('/n/b'), Node('/n/c')) Modifying the children attribute modifies multiple child nodes. It can be set to any iterable. >>> n.children = [a, b] >>> n.children (Node('/n/a'), Node('/n/b')) Node `c` is removed from the tree. In case of an existing reference, the node `c` does not vanish and is the root of its own tree. >>> c Node('/c') Adding works likewise. >>> d Node('/d') >>> n.children = [a, b, d] >>> n.children (Node('/n/a'), Node('/n/b'), Node('/n/d')) >>> d Node('/n/d') Detach/Attach Protocol ---------------------- A node class implementation might implement the notification slots ``_pre_detach(parent)``, ``_post_detach(parent)``, ``_pre_attach(parent)``, ``_post_attach(parent)``. These methods are *protected* methods, intended to be overwritten by child classes of :any:`NodeMixin`/:any:`Node`. They are called on modifications of a nodes `parent` attribute. Never call them directly from API. This will corrupt the logic behind these methods. >>> class NotifiedNode(Node): ... def _pre_detach(self, parent): ... print("_pre_detach", parent) ... def _post_detach(self, parent): ... print("_post_detach", parent) ... def _pre_attach(self, parent): ... print("_pre_attach", parent) ... def _post_attach(self, parent): ... print("_post_attach", parent) Notification on attach: >>> a = NotifiedNode("a") >>> b = NotifiedNode("b") >>> c = NotifiedNode("c") >>> c.parent = a _pre_attach NotifiedNode('/a') _post_attach NotifiedNode('/a') Notification on change: >>> c.parent = b _pre_detach NotifiedNode('/a') _post_detach NotifiedNode('/a') _pre_attach NotifiedNode('/b') _post_attach NotifiedNode('/b') If the parent equals the old value, the notification is not triggered: >>> c.parent = b Notification on detach: >>> c.parent = None _pre_detach NotifiedNode('/b') _post_detach NotifiedNode('/b') .. important:: An exception raised by ``_pre_detach(parent)`` and ``_pre_attach(parent)`` will **prevent** the tree structure to be updated. The node keeps the old state. An exception raised by ``_post_detach(parent)`` and ``_post_attach(parent)`` does **not rollback** the tree structure modification. Custom Separator ---------------- By default a slash character (`/`) separates nodes. This separator can be overwritten: >>> class MyNode(Node): ... separator = "|" >>> udo = MyNode("Udo") >>> dan = MyNode("Dan", parent=udo) >>> marc = MyNode("Marc", parent=udo) >>> print(RenderTree(udo)) MyNode('|Udo') ├── MyNode('|Udo|Dan') └── MyNode('|Udo|Marc') The resolver takes the custom separator also into account: >>> from anytree import Resolver >>> r = Resolver() >>> r.glob(udo, "|Udo|*") [MyNode('|Udo|Dan'), MyNode('|Udo|Marc')] anytree-2.13.0/docs/static/000077500000000000000000000000001500063244000154555ustar00rootroot00000000000000anytree-2.13.0/docs/static/buymeacoffee.png000066400000000000000000000207001500063244000206140ustar00rootroot00000000000000‰PNG  IHDR®(gåÁ+ ËzTXtRaw profile type exifxÚ­™ir#;„ÿós$¸‡ 17˜ãχ’ì¶»ûm1c…%¹TÅ�DfBöŸßð/~rî=m½ŽZ#?e”!“7=¾~ÆóœbyžŸŸiïÏÒ÷ãA>.e^óëÏ6ßçOŽë� >NOëûñÐߟ°àk¡ô¹ðó“ýÎþþ| ’ãò:žÊ{¡ñµŽÞ¾†ºÞ í÷‰O(ïßòÖëÅÿß4²t”eË)Çç¹¼"ȯßÉoâ™ã~�™5kxÕ÷b$äÛö>^cüš oIþx~Î~ž¿O¾Ì÷ù§\ÖwŽxóÛ’þt<Þ_¾Ýø3"ùþ í_¶óþ½÷ô{íµ»Y*­oD=ÉNËpâ"åù¹¬òhü*ïÛó þx¼Ë¿ ¨RA}ÒÜÙàŒëµÄÒô[ù©sæ<åõÕB)´ó^€qo%˜”©@¬)kª)6‘–yìh¹Ð‹ $U9)%ç*¡I¿7×´ôœ+*Uü0ÜD!4×ܨÍÈ“b•¢à§•†&-VTµjÓt謹–ªµÖV�äfË­4mµµÖÛh³ç^ºöÚ[ï}ô9dd8PGmô1Æœ&7š¬59rdÉÊ«,]uµÕ×XsŸ]¶îºÛî{ìyääMœzÚégœi)LaÅÔª5ë6l^°vó-Wo½íö;îü¬Ú»ª¿<þAÕÒ»jòTÊÏkŸUãhhíc‰ät¢^3*&%QñæÐâ5‹=•"^9¯YBS¨¤zmÂI^1JX,‰ÞôY»•û[u ÚÿVÝä¯*¼tÿ�ÊJ÷kÝ~Sµã¼ŸŠ½ºÐs3Ýwî”>C¯mì]­Q’ì+{Þ•Ž{ä.m�dC/×[^rö]‹M!ѹŒ3�ŽÚwWg9Bì‘ëí½;§y7›’LŒðšMv»# ¦~·ÌËvZÑ{g*kûpÔSv^³u y/g."—•Éq=Ò©Ng)€²/õ»™—F©çqY•÷¹5°0eÞ¹¥š÷îEäžsîRM´áv$›ä°¥Ó¢!’WfSn× ¸(r\B3_>÷„5ÙÕöô¸,7ØðiT´ž„\Ü©.ô<ýî5üÑß^ó€4ЬGµ*·u†e«ˆ“ìY�[ ?ýË+A™H/…x÷‹cÐgV¹�ÄÁÌ»YÓ¹ØjŸ2f8¼É±Ro¬ÉM’Õó¦ ,)»Yw•~(À¨ÂyìÈŸh}ž…¾eR»iU›–Ì'‚=-t»Ëã±1ü]Þ±YÉT|µê j£ÑºtmËj‘ ô˜ÍW €ÓîœÍ/s0ÍÖî¾[­¡Íe?ËmÕR.:HoŒ¥½ÓD¼½m!oi´H#me~2úgž“+»(z ½°ª´xXYëÑ‚öR^Ù7Ѷn+ÒXáÎJ¸4L‰—\µ¾ÄBsÞ¼–·W‡ºœn½š�½à/µŽ¼Y{£(Ð*7�‹íák>s¯:‹Ì•¥:†T�¬l�‰]#Ó±ô–A3É‚lµ2×ì7”sãâÊ�ž‰™Ö ßµ^b�eZ[:àCоÜËœ¶V!@ey’ÑÁJP;0Ò. N|%\hë–iÚOßV�Ý^h»d¸ÒHê> mƒäí�®™Ð4'îÝ !‚äÏ¢aµH .ÎÀ#‡æ®ÓÆ\¤:¶u°6ÔÏB�“…r°ÑFŽÖ‚)�p<Ñ´¹`#�QB¨Æ&lgº±©/ñÈÜ´„qâÌ´6¶Œô‘-˜(§vé†q»yï¶dÔ�,¥iEÅf2vPWbã¹N6AyBa«]Õ¡�4€½¹ÂYqÓVC”4Oøf;9æ8f*âzáÖ‚’U™ØÂ›Âüq³‰…Áú ùƒy¨ˆèbä1V÷mœÑjoäÚ0õOF9%£a;6•ë©Mh–(b$ºÀ¯ñÖ5õ¶�~mb+“}yã]:¬v"ô‹„/ßV úgy $eéc‚Žê)R¿èÜNý½W ŽíRÌTœ‹ š)¡æ›d­§Rs¥É ×nÐÍK+�a5No}ßHs¢Xœ×:j¾u„(t‡µ«k_‡3 y4ô¹¢�¤Õ�2ú;ŽBŽÎꨊ£™9š"IpÕÆöÆqwÈÜ¢G]³\c‰Kà a‘‰"ܨ½ÜŠÏÆÎ!¼Ë»7"©´t-Ñ&%`ÉK*†Ñ K)[¢4}…Ë —û0ÒHqîÅÍÅL-×km+JQQˆøÎ ¨:bR­ˆ†C3£64€u$i}f¨¢´3)°Ž�äÖNž¨SUÁ3è=ä wº‹‰§˜à´85!àEïœ 'ÂèÇ%nz„±×y„O&Œ;»¹Zè©ñüÔ8f‘ƒ®"Û'7bˆ³øÒ‰ ²î!œCá×Q’s�^�*/�M^×!8Âî¥}¡cöG è±xÓðz…\6ûf•y�[¢áÃKÊ;bDZ±câ§ |8/Ñd2[¹�FȺ®¨Á,7Ìã¦kPE3D�æDUð`´L®Ìð‘=�ŒŽúI³F÷_ºÅj¨.5¸^]Iž^š.Ž•T|ž±Ú.b;H܇ ôgw`µÁl·‡¤”óhþEŸï5œ&iP˜%ä&î±Ê‚ŸTptøâSÙ`o^Ù�#�hïHa¸)¹ÈÇÀ­Ù€Ò�ù‹(E¤llBÀ¿líäm?´§´Swe‡ÄáïÁ[×)¨rM‚"{³ ˆÖ<7à¾lÎZDÞñ¨ô.”JF³�ì­”H·Ñ!u»zdGi›˜íjPyccäjçMC90ä4] Y4ÒŽ- á8¢’Rãê Å u¼/$j†Ëë.n¹r^TÞIɧ‹ÖÃÖ4C š�Á‹¶/nIÁc€ú�ÌiÌÂÕH -Ã_e6²†½WoºÇd$¢Hn®èS눤×ݧ0?W‰«X‘áNÊÇTrŠJ'CÁ»4hÆì@¢t?(f Þs|Â¥ßÞÖ¥ :=c©Œ ]ÿ^!CTåÁ¾Â~Õí–BG8¶�Ã%�+c7¡æú8¥D¾�/s Âöî±°DÃõ¸# µ™‚Ì !ä5 �ö²”š³7³‘¸­)ÏR¢nhFŒ^ £sÇBZ ÜÂ`ÑF�nC'Z Õr.ŠXîå2Á`#Ûq Ý]É=Û¼pn­¦u™(MôžwÏ�­L R™§0ZmBüÑQ€°³˜Låuˆâš×©fhÂ�,©™aq€ޤÓ,lx7¡Õâ}]0Á˜‚ ª±^~š›0�¢( ’©ˆ1†–!döJò|ðc^¨ò-ÖA‘ø[ÆÀp•‚!yˆ¸¹ó©Ž T/K~´NüßÀ\ ¶¦Ó<øˆYkâZ2•;äÄ„Ak0ÓZb@�Ûâ¶U7zÄPIØÒf„(0ÝÅe!kð ®Ò ꚦ,Ü!ü^^Ø�zÆêãóSRàèF±6´Ð xÄ_Æ.�â3Рø깩F%JÁꀙö6ÈëâRÝ�F|Òv‰SYu¸µ\Ñg_V4}š”î£Oñ‰„k ª‡G0àØ†|«ÓÛµCÁ·´0û""U“+kF¼õòEû�!£Ë©Î…ÎÁ†àsX’wÞ@H‘ϽônÁ˜å`yVðï Ƀƒ%Æ–áe0’.†ð*n刉±µÖÃCù¸rí8ý]BWž9B]#êß@?8Ôñ‡faÓ): ©1Ü3Æ ëô?$‰-Ùؼþµ›aÖLì%aD\jf7F¼<7Ê v’ŠEÅñ0á®ÎücnH+Ó,9…¼+;öâ̼ €›8ÙÕª”©lJŒÈ^^‘|{£€#4 ŠVhί]ëš•ÓS[ >âH\Î`OŒi‰»“^ ƒVtÿ"ÉÞ÷À� ,(ÑrÃÍÔäàÚ„á ¹Ññ!øâ>.Ž˜Ý!e™a K„!l>é0î&½Ò\Ù[ XÑ àƒâê´àÕJˆ;R�O›>¿å %�©]Õl„‹ÖWšHp_›Ag¸3Ã52mapä=˜RX‡�±XAÙGÚ—AØ­u_ ™")p¹Š'ö¨Åm ›ó𙊃Ùëë‡æ#ÅÇ7Ê`|ž©ù(®@ÇäßKã%R‡ûPa” ½öDaõø Ð:èKA%îIb0E–Dn¡BŒ;]ŠlqÇéšÀÒPFxhäì‰É[¿~»BQgÔ(;A®nÈ@r�Ñ;YÇ+ãqü8;J÷ï|âÿö¦ÿÏ€|‡ÿbké´õùá…iCCPICC profilexœ}‘=HÃP…OS¥"U+ˆ:d¨ND¥8jŠP!Ô ­:˜¼ôš4$).Ž‚kÁÁŸÅªƒ‹³®®‚ øâäè¤è"%Þ×ZÄxáñ>λçðÞ}€P+1Íê˜4Ý6“ñ˜˜Î¬Š�WøÐ‹D1,3˘“¤<ë랺©î"<Ë»ïÏêQ³|"ñ,3L›xƒ8ºiœ÷‰C¬ «ÄçÄã&]�ø‘ëŠËoœó xfÈL%ç‰CÄb¾�•6fS#ž&«šNùBÚe•óg­TaÍ{ò³úÊ2×i� ŽE,A‚Q‚�í:)’tóð5ü¹rÁȱ€24È ?øüž­•›št“‚1 óÅq>F�À.P¯:Î÷±ãÔOÿ3p¥·üå0óIzµ¥…�€¾mà⺥){Àå0ødȦÜ�ü´„\x?£oÊý·@÷š;·æ9N€Í*qcyÊ^÷xwWûÜþíiÎïŽ\r²óBÖÔbKGDÿÿÿ ½§“ pHYs  šœtIMEä7’ê-ÆåIDATxÚíœytTešÆµ$•}ß ’°»v±PD‘Õez†žluÔÓNëi—sÜš±Ñn—ifÄ¡µéqDPQ‰c��$ Kö„,•¥öª{ç�÷†J ‘$¦ûÔ{N’{îR÷~ß÷|Ïû¼ÏwS:õÉ*¾ðÅßXè}]à p}á p}á p}ñwÆÞ^àQTܽ(çL�¯—}ñÓWf¼SŒiÄdüŒ—¿´òüyÖ§6scz”¯§}ñÓ÷PU+“—ÜɳÏ<ƒ¢(—=¿¼¼œGo¾Ž9i‘õ>æõŵ®N¨l)¬dá3 °Z­¨êåõBBB†±7p¢®”щ¡mûlc_üøÐ]n¢Åáæ±O‹Q� ¼z L&‡£Ç70 ÌŒô0:Ù@dx�ÏNÅǽ¾¸êŒëQTΤÎáõ×ó›q#›ÍÆêÌLŸ�Úû‹£Ó!>Šw€« ô:PÀôFß(^uzÓAÚl¨8ö¦ÎÇR¦@K˜O÷?©ðIþa^zé%Uí3[ºÝnücûr¥ †AÃàèdˆŒ‡°‘%ðñó`oö�ëjFÖ­pËýP¸>|tš‹ž ÷<µgà•;ÀÜ€j2²}š[>}‡‡ï3¢$Ww’©ê…_äpÇÉÚ�á©�Ù|çäÞ_g€@­CFç€â—LA~$ý ^[ n—`W%TH¥mê¼ HÒö»�2Ný‰q�z£âCø@_Ãè„(^Ý»;œƒ’LJd (½,Îô~`2‰4Øû(9ÖZˆÎ€Åg¯„oþÛ‡±«ŠB´Âº¹¶ó±ø ùki“qº†Ñ£•³”ð ªÛh©í%_ê€Vh±CVBH'fîyugvu»`÷P[­5pv/”|#ç„Dxi_qrÉ�<¶În†ªÐ·‡ÑèÂg© Ø@µkŸÙñ'àºhÇž×CÒ$ˆª»‡ r‚ÛÒýgv¡Ü+*TO7§¸Àݫ֦ý!ÛM•š¨Šd“¦kÆÕ©�4bG€zQF4øÁ€)Ú3)½…i\�AGp€¶=aº^Žy”Ô˜„оYa†s-`êÜqQ‰²}¾BfIH,| ¿€Ÿh:l ܶò¶AÑGRÌ-x NBÑÖ‹0é‘Iâ°uN‰Ž»aâÝ û^�ÑK`Ær0úÁÑ}°û?äóÇÞ×ß#äm‡;k¨¨4Xþ4D'‚Çû?€½¯ü@ºU!y2L_¡Qpúä¾h©½TŸuŒÈ¡°lÄ&ƒË _o…}¯{QU`HLYq`ô—ÏÛ÷>þ „FkŒ[Ó¹/‚‚4ë©¶›ûþb€Ç y;`÷zicÂXú¨dK—vo‚üw¯ ‹ï‡ì¹�5]XlßÿB@L^@ñvЛ s> *ä�5ðÏÁ?L`ëÂÑyàÖ‡´g‹ p7L^ ƒD>]lOuŒä 00S&÷ÿ<÷½n‡&§€´Y0c™´=;L¸ šë¡ú Œ˜×C±E&€â{k‡±µAHT׌;`,  mMð‡åpïf0‚_dÎ…˜P_ o®�û6 x;re·®ÍÁM�¾LVVVŸHs‰ÁÀê»ïáC^F‹Ðä@@°üØ-ÐÖ,�£r@y>z¢ã…ýªJT©’ZN烟Ÿ°ÀÉ‹XjØ�/ÛÇ·w£KM`>/À5¡òìþ�Lˆœ¥Â„í“bÏïEJL^ ¬¸@g„‘3äœÂ½àvƒÞNXj@x©D˜¶VÚ`®ƒwÖÊîÔ \k Ôä�1¢ky1f¾lžÈS¤ô™¥Y²…Ûsî’}yñ=2ÁÊŠáÈNnÊX¨<&çØ-–ú ãÑž ;ÖãËöÑ\p¶ˆ”²[Á^™S娱\pYÁß�5`¹ÂRA…¬„PÎûû“��Ýg7¤¹†0SZ€;@þ–äÃÁ­ÐP*®‚Ή`Åã�•y£E3©ªnCgKZwX ñÄ�“NtÚ ©¼Cñf…-]ºPŒ.Mg�^Ž·Ç7ÛŒzaâvàî_öµË§UžW±CœvNY1L_#…eá^Ðùw­©‡M–íCŸËdt[!>Yö•—‚^»G@˜øªµEržÇqZß}j�ÃþÿƒêS2‰¢‡Ê³8­°ç?…õT‡@�ÝÛÞÐD)$6)rÛ#(¡k&öØ!6I¶O„YI†,: ÷ŠÒúµäsXø8øBi�Ô2WÚUˆ5ñÝwßõÙº¨¨¨`P�‹ð >X&á žÈ…sû¡­x(?§�ÊñÓ¤RµµBó9�™ÆjŒÐ öMƒA´¥£CŠÍZ‰ƒµ´wôšœIŸ ©3;›Öz­Ëœ68¼Ù«§<š3ÐÖ%;d;a¨Vu· Àý‚¼žôÂ_À´[…w®×ôæ¯ ë¶ú>£Œ:ó�·Ølù a>U…Y÷Â?¼í¯‘*.ï½Ìå2qö¼ ÅZÑ:`‚¬@ÖU�³ê‹„ÕãR`ú*­>i‚ˆ$Mv9…©Û#8A´UV3/·"µ,yÓ¿Àô¥`³ÀgÔ&B¤»ã 7šÏÃ×ïŠ|™÷Œ¼¹GE|�€›fâLYn·»OÀ5›Í � ì½¥x PKŸæÊ.Ž[ ^KýÚyM5` Æ:FŽE:=:I@¦(^[(0~ös¯fvÚd&ÀÒƒùk:¤H�èQ€²ojWðÓ�ò“` ”}‘Ú¤kkà%Hj)N*J`ÓcÂXA‘�³ÒÆyÛg —óÝNh>-Ÿyý?z�—}«9/Ñ”­�àhéb”�—¦Ñ‘9ÒN‡ÅŸ¼.pèxmBçÀ wÊ¶Ãæµ­‚£añcB»ÈŽvûkù["á@ŠÁªS°éß¡¥J²_{ŠLIµé7`o„„l˜x¤�§û*µ—7&ÄŸüܯ0›ÍÄÄÄô¸ÅÅÅLÞ{;LuA°ÖPsÕE¶Uܸb’e`kÎj µŠí1a™¤¿ö  ×CölÍ÷ ‡¤ 7þ‚#„)C"E»¿n}@dÅ¡ÏÁäe±`MOV{Á®ºäZ€ŠÕã�Lv-ÅNZ.ºÐÚ‚SP© Üò„ªàãìå�g7úCD ć±³:86õš>Ÿ/v×–I `‚Æ:ˆN†”ñPuL �f=Ã'iàŠ£Il¹ï?‡Â)2UE€Ö>dÖf‘fYs {økms»¤–ÈÌ�Ió½5…µþ¸ ¬M²8¡8añsrÿ¶&x~ c…$T7ܰFú"÷�­ÂõÌSaQx5‹¥OÀ-¯¬$3ØÔ{ªöؼ€˜w?œ>¬yšÃ 5 ¢¤ƒw¾ õš<ˆˆ…aó`Ö ïç$†µïI5ëq�_Üõ´0@í9øâ-Xñ” ô�H >~þWGµî˜ª“� • ͪ9ãµ¢.,C«�1¦.òšñS òiãâg!cœ*„³û¼«PmUÐtÃʧH?hm’”›µ@ªó…k%ÍبÙsÚ3�*€ôl˜~„ÄʤL- ?[$Ú3.Vm€CŸJÑ—u½\ÿéÛP²¦®”sèXȯIÊN@ÊpˆJ€_¼ìµêŠrÅÉ‹�èL°h¬ƒš^‰„؉`®�>]ºÒÆ@á—P_z%�«2wäPNž<É Aƒz…=—ËÅׇóËÁ�}X¤rЍx 3ÛÛðö"êìqØýT«Åi^ñ[9ç𜌠Úï !w ,¸Wö;[àÄAøêMXö¬ÆTFI—ŸÁ¶uâ$´GÈjçJ#Ù+¾eÕ¡öU‘ ‡ÁÔpÝBðúJy–%ÃÙ0x¸H s|øôEK§zؽQÎ ý¾ÿϲpû#0çn¯¼ùèåÎàÐ_aô H΀iÚ¤i3þ-ðÅ‹;îzAdÆà‘Z¿ÚáË÷!o£0`;³¶Óí„·„�ã¸zƒü€xÜŠ[®Ó]­ÖVaõ+�: vl„â�pÿ&qB vìåðíNøäY¸ DÄÀóó¥`SœÒÆàh(Ï׊^í9‡,Ž˜B ,Oüì^¶¡ÇK¾±!þìºw:éòæÚtb¡ÓáýÑRUEÏ«ßÂî/¡®6�Uc¯ÝkC†^héqË5ÐZáƒçDG:l—IWº~7à’ŠÞÿÒI¤3BÍñÎþp_âZ¼Doð[ÄŠl¨�`òº ¨²Ú ‰Ùpökismq×mÔ› êè�j�[®üŒz¦&‡¢Ôùãgìziü‚Éf�³��ÔÿQrê-ÚBÂ' ¹­­¾ÿª¸x™[E–ŠQÁãñ¦xk¼>¶_à5y¬ÞA]UY�Ey-`ÿ¡Æ6¨k…˜`ÿþ9 ±é¢³œvørdLÑì¬"oeî h­F ‰¿`ˆNðšµnLšU—,À6Ÿë‡ÀÕVѪ,h ëþ$ 8\°«†DôÏ¢­™W–‚!Ò²„ENåùÀÚ1ÏVköÞ˜°üyq*K¡ê„ôWF6,~Jl5[TÛ?�›@y‹Zº?É nò+ýñ7ôÓoyri«aQ‰âc†BÕ÷ÐPìÃkÇ8ºULfŠe¦ªðը̃º2±²²´‡l“¸þÜä0)ª§±RGƒ™®ª¡¬ÌŽíDzM^á �•±µÁŽW}2áâ°7Ãæ'eÁ¡® ¶¿§÷H�õîÃò‚L}•¼ ¿ÿO׬ÿt½ýbg«KaùûG9uÞ mj×Ï ízbÖ nß%8A<Ì€`8º Ìg|@í.>>!!£€˜y€p£´´4222;;ûÈ‘#={ölݺu||ü£G�æÏŸÏ{4„X�Bspᚈ‹‹€øøxÀ(è,‚©� FÁ"莺r@Á(è,‚¾¨?Œ‚žÁ"èWr@Á(è,‚^p=Œ‚>Á"肆怂QÐ!Xíã^(½�EÐ8�É£ +°Z¦ñ9 `ôA³(• FA'`´‰²9 `ôAƒ¨‘ FAó`´†z9 `´ AS¨� FAÃ`´›P0 Z‹ X怂QÐ$X-À>Œ‚öÀ"H¯P0 ‹ 7|s@Á(h ,‚Ĉ� FA3`dEœP0 Ú‹ %¢å€‚QÐXù3Œ‚ì`$CäP0 RƒE� ñs@Á(È AdÉ£ )X9�+Œ‚Œ`$@ÆP0 Ò�Eys@Á(ÈAhdÏ£ XqÑF(YÀ"Š–r@Á(HAD´— FA|°¡ÕP0 ‚ƒE m瀂Q,‚@(’___›Íæø«Á`ð÷÷oݺuŸ>}bcc© FA\"6›mРAM›6=wî\#*;;¢¢¢!UUUyyyiii €˜˜›Í¦Ä¼ �’’III¼A~‹  æ€ü´ÎüéO€ÈÈH»ÝÞø­(FA4°üQ6¤î"Øíö_ÿú×°sçNE6¤¡0rÛ]A€íK‰ƒaæÌ™°iÓ&U7Ô âââRRR,X°lÙ2Þ³ €Eà ûwúöí §OŸ®¬¬€ªªªÔÔÔÁƒ·lÙÒËË«k×®k×®µÛíôÎiii†øúë¯Ç�8|øð¯¾úJÁ©0 ÁûIŠ~Q|gÁA]{ „�²²2úsÏÍÍ%„X­VHJJ*,,ÌÏÏ_·n�ÑhLHHpþ’¨¨(úhYYY%%%GŽñòò Uvf‚»b€Eàƒz9 õ¡´´´Fœï0qâD‹ÅR\\츅Ájµ:n=z4äçç+>9F�;¸×À�²²²ˆˆˆœœœãÇ�3þ€ÀÝ»wÀb±4oÞ†~üøqç;tëÖ­²²ò‹/¾¨ñ…¡¡¡Ž?‡„„@nn®âãÅÅÅ%''/\¸pÍš5Š?8â X\¸p!##còäÉݺuc¼é“'O@ïÞ½- /Z´¨k×®M›6¥¯$&&€ã©„ÇŸ=<<Àñrƒ²¼ùæ›;vLNNVãÁ‘'‚Eà@Ÿ>}RRRV¯^Íø…4»Ý¾qãFøÃþ@o‰ˆˆXºtéÔ©S¯]»F?¤@Ÿ·BX怾ԚŸŸŸ––Æe?ÅÌ.ã�?þ™3g¢££ÇŒÕÕÕ™™™-[¶œ={¶ã>Ž—Ù£‡c:Ä‹À 6Q°Ûí§N�Z·n]FFÆ”)S6lØ`0Àd2………edd¬Zµêõ×_÷ññ9}úôæÍ›Uš¤~0¢Àû¥M½£ø«ë>>>Î?_z¤S×®]§OŸ~þüùwÎÏÏŸ6mZHHˆÅb Љ‰™7oýž={ž:uÊù¡.\H~º7®È̪¾ó‚4,tþ–æ@(p¯�?z>4wD‹ úŒæ@@°¢ ·(`Ä‹ ú‰æ@X°b¡‡(`D‹ ÚŽæ@p°"¢Õ(`Ä‹ (Ú‹æ@ °⢥(`d‹ 4Úˆæ@"°¢#{0r�E�y£€9�,‚ÈÌ�Œ`¤A®(`$‹ ²Ds /XÉ? ˜©Á"ȇÈQÀÈAJÄŒæ@`dE´(`´Abĉæ@;ð>Ñ#ÒX† \OÜj³Ù àáá±}ûv^3 J�E�›;vЋ/ðŠ‚Íf 3›ÍðôÓOß¾}›ý ˆ‚àUÞ$&+++66–b4ccc,XÀø²qtgáâÅ‹C† €»wï6¬¸¸˜å ˆÂðNâ&7oÞlÑ¢ý!&''æ×}p¾ÎBEEÅ€è0Æ «ªªb3¢8X))..îÒ¥ ý ¤O(Ì¢Pû²+;v¤#ÅÅÅ©=¢Xù¨¬¬sæŒÚ›CA öîÝk4ÀËËëôéÓl6šœœl0:uꘓ“Ãf£‹/¦á ¾sç›�"®€E…³gÏz{{Ó�÷íÚµ‹å¦»wïîåå•��Àl‹v»}âĉ4 =zô())a¶i¤~ðÌ«BpëÖ­áÇ—––À²eËÆ�Ïrë999eee·oßf¶EƒÁðÁ¼øâ‹���=nܸêêjf[Gê‹ÀŸ‡FFFÞ»wbbbæÎ�Ë{"xzz8pàÙgŸ€ôôt.K…�;ÕÕÕ&L¸téôë×oóæÍ¼'bG``à�`ÕªUºúÞ…‹À™¸¸8«Õ :tøì³Ïš4iÂ{"¦üâ¿HMM¥€™={öÑ£GyO¤w°|xúôé¼'ÒXu©¬¬;vìÕ«W`È�!›6mâ=‘L4kÖìСCO=õüío[¿~=A]fÍšuìØ1xþùçwïÞMéA\§]»vŽÀâãã8À{"�ƒEP‘+V¼ÿþûмysÇa¿HCéÛ·/ÕX]]=qâÄ‹/òžHË`Ôâ³Ï>[°`üpj�:ðžHb&OžLe>|ø0<<üÛo¿å=‘fÁ"¨Â… &Mšd·Û ÃÖ­[{÷îÍ{"éùË_þò»ßý¾ýöÛ¨¨(›ÍÆ{"m‚EPžÜÜ\Ç’]²dÉ«¯¾Ê{"-@Ûú›ßüΟ??yòd»ÝÎ{( ‚EP˜’’’ðððo¾ùÆ�·páBÞi//¯}ûöµiÓ>ýôÓwÞy‡÷D‹ $ô¤ã999зoßmÛ¶ÑK• JѲe˃úûû@RRÒ–-[xO¤5°JòÇ?þqÿþýðÓ÷Ìeyþùç§fœ9sfFFï‰4A1¶nݺfÍøÙÏ~fµZéçj52dÈêÕ« ²²r̘1×®]ã=‘vÀ"(Ã?ÿùÏ7ß|,˧Ÿ~Ú¹sgÞiœY³fÍœ9 ‡ VPPÀ{"�€EP€ÿýïãÆ�£‡â¬[·î¥—^â=‘.X³f =fÌù@2¤‘`‹óẉ‰‰ô™Âz\ù/ùKø×¿þ…æ‹Ð(ÊËË£¢¢¾üòK_¶lï‰ô…ŸŸß�‚‚‚àã�?^±b‹à>„�©S§fee@�=RSSM&ï¡tG›6mÒÓÓéeµçÏŸ¿{÷nÞÉ Á}Þ}÷ÝO>ù‚ƒƒ8€'Mä…ã¶„�)S¦üûßÿæ=‘Ä`ÜdÏž=K–,ooï´´4zNq„£G�~÷Ýw ¬¬lĈ,¯|¯1ðp}wÈÊÊ¢—1�;wî å=Qƒ)***,,t¾Åf³Ý¸qƒþ9$$Äb±ð˜Ë}Þ~ûíëׯoß¾�^ '33“~´ii 7oÞlÑ¢µ—œœÌ{7iÖ¬Y=«bÆŒ¼t‡ŠŠŠÐoaذaUUU¼'’ÜkhΗ*���¥—r•‘ÐÐк^5 2>ë�®©Û±cG8tèPBBï‰$„w’d¢²²rðàÁÔ[ÿþý=zÄ{"÷ùûßÿ^×å¤,KQQïÝçÊ•+M›6¥ßËÆ�y�#X„0cÆ ºÎž{î¹ÂÂBÞã4Š’’OOÏÚ90›Í#FŒà=]c9qℇ‡˜L&«ÕÊ{™À½WY½z5=“r`` Õjuü/$)>>>µ_>¤ç2ä2’‚üö·¿Ý¼y3TWW¿úê«ÿùÏxO$¼“$ééét¯ÛÃÃãøñã¼ÇQ†´´´Ú§oðöö.--å=š2$&&ÒoªmÛ¶yyy¼Ç‘,“ÉÎÎöõõƒÁ°}ûvÞã(Æ£G�üüüœs`±X&MšÄ{.Ũ®®ŽŽŽ¦ßÚ¯~õ+›ÍÆ{" À½†'p÷îÝÈÈÈ’’X´hÑk¯½Æ{"Åððð3fŒóŽCeeå„ 8ޤ,F£qÇŽ½zõ€sçÎÅÄÄBx%<¼“$4¥¥¥t=À˜1cìv;ï‰æèѣ΋!  ²²’÷P “››B¿ÁÅ‹óGt°uâüœ344T“Ï9«««çzòðð˜5kï‰TA«û}j€{ u2oÞ¼}ûö@Û¶m­V+=ºNc�Æ &Ð‡ŠŠ zAíѽ{÷={ö˜L&BÈo¼ñùçŸóžH`x'IP>üðCêÇÏÏïÒ¥K¼ÇQÇ‘‚ÁÁÁÚÛ-r&99™~§���ׯ_ç=Ž `ƒÞ>ßB¯€°`ÁÞƒ¨Ž–>c¦Bï5”——gff2ÞèÕ«WGŒQQQëÖ­>|8ãØC¯:5~üxÞƒ¨ÎÚµkéçЯ\¹MÊÌಞ ï$Õ‰Íf8p °=¾°  €'ñññ̶Ë›ÍÖ·o_Æž9R\\ܵkWú#Ž��e¶].ëÙ -Õo0ØHÔá±´\ ‰K—.¥9xúé§oݺ¥Þ†�—gqHMM¥‡uxyy�9sF¥­HçY¬"¸€ðò, ôZÞ4 =zô())QðÁ%õ,Jž¨�¢¸Ä¯¿þš^ÿ–/_®Ôà /ÏÂRVVöâ‹/Ò®ÔËÉòz¢.ê£4Fâ­[·rrr}ðà½FÐã´ 3ÏrQPPðì³ÏÒe�˜˜è¸Ýn·:tÈ�C¿¤öÌ¿ ÒGq[âàÁƒÍfóG}D©ªª¢×€~ýú•——7ôÑä‚¥gé¸|ùr@@] ï½÷!¤¬¬lìØ±°uëÖ=”ìž9Á }7$Þ¹sÇq®ÑyóæÑk�@‡òóó8¸d°ô,)‡6›Íðà �CCCÍf³Á`èÕ«—ë¢Ï<‹à¶>JC%.Y²„þÈé»Ðô€¿ÀÀÀk×®¹±u‰`ìY^Þ{ï=º<<==�O$sùòeW¾\ž¹¡‘ú(®K´Ûí­[·vþø¶Ùlöððؽ{·Û[—ÆžegäÈ‘f³Ùñ?}Ê0wîÜ'~¡f<ó)‚"ú(.JT|Žðò,5ׯ_9r$<öâw&“)77·Æ—hÒ3»"¨ª�R—ÄnÝºÕø1Ó@ :ôÆ�* Ã Žž5À‘#G:vìXûÿ³Ù¼råJç{jÕ3£"0ÐG©-ñÒ¥K5~ºF£±]»vøÚAcÐp***Ö¬Yãíí]ã…§öíÛ;ÎC©aÏ,ŠÀL¥†Ä·ÞzËñ£µX,¾¾¾Ë—/—ú²ÎuÁ׳ÆÈÍÍ}ã�7 ƒó;‘™™™DëžU/c}‡Äòòrú¬Ï`0¼þúë÷îÝc6Køzf¶EÆdffÒ]NƒÁ`2™¦L™¢yÏê�‹> •è¸AÏž=Ïž=Ëxfp÷¬á(TWWoÙ²…^ ÜËË+,,LÛžU,ÇeJ¡}||>þøc _‰@ÏŽ!äû￟>}ºÁ`ðóóÓ¶gµŠÀ}™RV®\©íÅ*ˆgÍG�zмgUŠ È2¥hx±¢g6èʳòEJE“‹=³Aož.‚€ú([¬è™ :ô¬d„ÕGÑÌbEÏlЧgÅŠ ¸>Š+zfƒn=+S)ôQ¤^¬è™ zö¬@$ÒG‘t±¢g6èÜsc‹ �>Št‹=³=7ª’ê£H´XÑ3Ð3iL¤ÖG‘b±¢g6 gŠ›EЀ>Šà‹=³=;p§šÑGv±¢g6 gg\�飸XÑ3Ðs VMꣵXÑ3ÐsmP 룲XÑ3Ðócqµš×Gá¾XÑ3Ðs]¸TÅõ=|ø°Æù‘û÷ï¯È#7Ž‹UAÏ"¦hÃ3^uC=?¹ªÖ4((Èd2)þ°�„ËbUɳ˜†)ZòLVÝ ÏO(‚ÚO®Ü“èããÓ§O5æqÀx±êp™R4㙈­ÚuÏõ�Á¾–°E «n—)Ež‰ðª]ô\gؼô"r“ŪóeJÑ€g"ƒjWôÌ�yæP¢¨D\¦õ€žÙ %Ï|Š@’È]Ÿø g6hÆ3·"�FKAŸ g6hÃ3Ï"�FHDŸ, g6hÀ3ç"·$Š£O"Ð3d÷Ì¿¤�…Ò'è™ R{¢Äe‰¢é“ôÌy=‹Râ‚DõÉzfƒ¤ž*©W¢˜ú$=³AFÏb�Ô!QX}ò‚žÙ �gáŠ@jIYŸÔ g6ÈåYÄ"'‰‚ë“ổ< ZBHrr²Á`èÔ©S```NNïq4 zfƒ,žMþóŸAHz÷îíïïâĉÇwëÖ�÷8š=³AÏBï#ï,‚ ?‚E@äG°‚üÈÿ§²ž²dèñIEND®B`‚anytree-2.13.0/docs/static/dotexporter0.png000066400000000000000000000603411500063244000206260ustar00rootroot00000000000000‰PNG  IHDR�[?@#µbKGDÿÿÿ ½§“ IDATxœìÝy\MùÿðϹK{÷–v‰R’R©TZ,-’’%ûRÈ>(_Œâk03fˆa c C #»TD‰¢= iIÛ¥}ïÞûûãü¦o“´Ý{;wy?ÿðÐétÎëö>Ý÷½çžÏç`l6€/‘ˆ€o‚> ð/èÓÿ¢¡R__ÿéÓ§ªªªêêj„Puu5‹Å’���””¤P(t:]YYYYY™J¥�  OÐO���iiioÞ¼ÉÎÎÎÌÌÌÏÏÿôéS}}}o~VIII]]}äÈ‘zzzzzzÆÆÆzzz†ñ:3@à`p½7½W__õøñãøøø´´´ÖÖVYYY¼×êêê2DYYYMMmРA4 !D£ÑÈdrcccSS“ɬªª*//////..þøñcVVVfffAAA[[Û Aƒ¬¬¬¬­­�œœÌÌÌ gpЧèƒÁ �‰‰iii122²±±±²²²²²>|8‡oiiyùòåóçÏãããccc?}ú¤ªªêêê:kÖ¬)S¦P(pÒ ‘}€ob2™aaaçÏŸ svvvqqqqqQQQáÝNÓÓÓÃÃÃïÝ»—�� ¢¢²xñb///}}}ÞíÀÏ OÐ…æææ«W¯þú믹¹¹VVVžžž‹-’‘‘È EEE—/_>sæL^^ž���¯¯¯››Û@ðèÓüKkkëñãÇùå—ººº¥K—nÞ¼yĈæa³Ùááá|ò䉥¥åÁƒÇ�O`ÀƒñÓüÏÝ»wG�íçççååõþýû'NÛ¤B†¹ººÆÄÄ$%%Ñéô &Ìž=;??ŸØT€}„b0óçÏŸ9s¦™™YVVÖþýû•””ˆõ/æææ<ˆˆˆÈÎÎ622:rä‹Å":€çà¼7(<<ÜËËK\\üÌ™3“'O&:NZ[[ýõ×_~ùÅÚÚúòå˃&:€‡àý4upsssrrÊÈÈàÿ&�¢R©»wïNLL,++377OLL$:€‡ OÑÅd2—/_¾cÇŽC‡Óét¢õ�‰‰IBB‚±±ñ¤I“nÞ¼It¯Àyo ¢X,Ö²eË®_¿~ãÆ�©S§§Ÿ˜L¦··wPPÐÕ«WgÍšEt÷ÁTG@D­]»öÚµkwïÞˆsÝßB&“�;†š?~hhè”)SˆNà2x? DÑÉ“'ׯ_ûömá˜9„Íf/]º4,,,99™óyL|ú49©©©666~~~{öì!: ×466򯯡ÙìÄÄD111¢ã¸ú4-L&ÓÒÒ’F£=zôˆDªë(óóó�ŒŒvíÚåëëKt×Õó= ÊÈÈ8vì˜�5i„�¶¶¶ŸŸßÞ½{‹‹‹‰Îàa{ª L&sß¾}ß}÷Þ~JFFÆÖÖ–Ã�|ÿý÷òòò¿ÿþ;W"øôi BîÞ½[TT´~ýz¢ƒðŠ„„ĺuëΞ=[__Otw@Ÿ"äâÅ‹S¦L!üÖ<µråʆ††ÐÐP¢ƒ¸ú4,ëéÓ§...ýøÙÛ·ocÿÈÎΞ7ož‚‚þ%ƒÁ@}þüyóæÍÚÚÚbbbòòòS§N}üøqÇ-t³Âo¿ý†aX}}ý³gÏðmR(ýŸØ@IIÉÜÜ<&&¦ß[ðèÓ@T¼zõêóçÏ'NìÇÏΜ9“ÍfϘ1!´fÍšï¾û®¨¨(!!�L&#„JKKÍÍÍ/_¾È`0¥¤¤Μ9ƒÿx÷+lݺ•ÍfKKKã«Ølv[['�tÒ¤IOž<ád >Â@4ܼyð–––~oïÓááá�–/[¶ !tåÊ•ö%MMMƒ–””,--íÍ ì÷i]ºtILLŒÅbqekbÁûi ****äää¨T*‡Û±°°è´äÖ­[!WW×ö%âââ���<èÍ Ü¥¬¬ÜÒÒR]]Íõ-ôi *êë륤¤8ߎ´´tÇ/›››«««%$$dee;.WQQA•––ö¸瑺LXWWÇõ-ôi *>þÌõÍŠ‹‹Óéô¦¦¦ÚÚÚŽËËÊÊBªªª=®€‰a·"á—¶)**rkƒAŸ¢BII©©©©¦¦†ë[Æo(Ö¾¤¹¹9**JRR¿�U�+ „¤¤¤ZZZðÿ�9òôéÓýÎS^^N£Ñ$$$ú½ÿ€> DŘ1cBIII\ßò¾}û´´´6mÚtïÞ½ÚÚÚœœœE‹•””â'·{\!djjš““STT_PP0~üø~çIJJ266æÎc ú4jjj#FŒè߀¥„„ Ãîܹƒ’””ìtŽZUU599yáÂ…ÞÞÞ õõõ�=ZµjU/W@�5jþüù���£F�ê÷#�‰‰éßð3‚ûeâííýàÁƒ¬¬,.~ÌoRSSÇŽgccCt@Ÿ"äíÛ·<˜ DÑ«W¯¬¬¬/^|òäI!¸ö»´´t„ ÊÊÊ�?†3Þ!y3@Ÿþõ×_sçÎ¥R©G�èV]VVfooO"‘®_¿Máóœ5cÆŒ¿ÿþûôéÓ«V­jŸ°SàäååMš4©­­-::º}ªp€0�> D—»»ûÍ›7CBBÊËˉŽÓg�=²´´”––~üøñàÁƒ‰Žà èÓ@¤M›6->>¾¤¤ÄÄÄ$<<œè8½ÕÒÒ²sçΩS§:99ÅÆÆª««�À+Ч�¨300HNNž4i’««ëÊ•++++‰NÔƒÔÔTssó€€€#GŽ\¾|YRR’èD‚> ’——ÿ믿nܸª££ØÚÚJt¨.|üøqÙ²errr/_¾\·n�@_è èÓü?ww÷œœœU«Vùùùéëë577êÿ}øðaóæÍ#GŽ|úôéåË—cbb´µµ‰0~€ÎÞ¿¿wïÞ‹/ÊÉÉmܸqùòå^¥üøñ«W¯ªªªnÞ¼yݺuâââD… <èÓt­¤¤äÈ‘#§N�ª®®vrròôôtss“‘‘˜½^¾|ùÂ… 999FFF[¶lY¸p! �@AŸà›X,ÖÚµkÿüóÏ &<}ú”L&Oœ8qÚ´iNNN#GŽäúîš››“““ÃÂÂîÝ»÷úõkEEE}}ý„„„ëׯ»¹¹q}w�}€®1™Ì•+W^¾|ùÊ•+îîî÷îÝ‹ŒŒ¬®®VTT´²²7nœ‘‘ѨQ£455Édr_wÑÐÐ�••••••’’’���––ÖÜܬ££3mÚ4WW׉'R(ŸãÇ�Ÿ={véÒ¥¼x˜>}€.´´´,\¸ðþýû·nÝrrrêø­ÖÖÖÔÔÔøøøøøø„„„¢¢"„�¸¸øˆ#ÔÕÕUTTTTTèt:‰D’––«¯¯oiiijjª­­-///---)))**zÿþ=›Í¦R©úúúÖÖÖVVVÖÖÖ�.c³ÙÛ¶m;|øðÉ“'á®Òˆ èÓtÖÐÐ0kÖ¬¤¤¤{÷îÙØØt¿rMMMvvöÛ·oóòòŠ‹‹ñ6üåË—šš&“YWW×ÚÚŠwkIIIeee555uuõ‘#Gêëë>¼Çžýýý·oß~ðàÁ-[¶pï�ôiþ¥ººÚÕÕõíÛ·DÇùŸ?þøcãÆ�Û¶mÛ¿?ÑYî—Àÿ|ùòeêÔ©EEEOž<=z4Ñqþeýúõ4ÍËË«®®NÐïñè=èÓü¿ÒÒR''§ÚÚÚØØXþœEÄÃÃCLLÌÃã¶¶öìÙ³ üý üàï„*,,œ}ÊÏMçêê6kÖ¬ÆÆF¢ãx ú4uiii&LÐÔÔŒŽŽVRR":N¯Lœ81:::!!aêÔ©µµµDÇðôi Ò’’’&Ož;vì“'Orss>þLt¯@Ÿ¢ëñãÇŽŽŽ666ááá6q7DGG—––Nœ8±¤¤„è8ž€> DThh¨‹‹‹››ÛÍ›7%$$ˆŽÓO#GŽŒ‹‹kii±³³ÃgFèÓ@]¹reöìÙË–-»xñ¢ �n:thll¬¸¸øøñãsss‰Žà2èÓ@äœ:ujÉ’%›7o>~ü8‰$ ***111jjj&LÈÈÈ :€›„áI €Þ;pàÀºuë¾ÿþûýû÷ Ó”^òòò>400˜4iRbb"Ñq\}ˆ??¿C‡ åÙ222wïÞµ´´tttŒŽŽ&:€;à>@$°Ùì-[¶9räôéÓ^^^DÇá¡–––Å‹‡‡‡ß¸qÃÙÙ™è8NÁûi ü˜LæªU«þøã�+W®w“F‰‰‰ýý÷ß ,˜1cÆõë׉Žà”`_é @�ZZZ<<<îܹ2cÆ ¢ã 2™|æÌ�¶`Á¡?€Ðƒ> „Yssó‚ >|æàà@tœ�ƒaØï¿ÿ®ªªºråÊšššM›6�ÐOЧ�Ъ¯¯Ÿ9sfjjê£G�Æ�Gtøúú"„þóŸÿTWWïÞ½›è8€þ€> „See¥««kAAALLŒ‘‘ÑqãëëK§Óׯ_ߨØ(”W¹ ô O!TVV6eÊ”ÊÊÊØØØ#F‡`k×®¥ÑhK—.­®®þã�?„cjDôi l>|ø0yòä¶¶¶˜˜---¢ãð…E‹ÉÊÊΛ7¯¶¶öüùó‚>U*"ÆO¡òîÝ;GGG ‰‡<˜è8üåñãÇÓ§O···¿zõªàÞzQgÀ€ðÈÌÌ´µµ4hÐÓ§O¡IÍÎÎ.<<üÉ“'®®®uuuDÇô ôi $RRR&Nœ8bĈ¨¨(¢ãð©ñãÇGGG¿zõÊÅÅ¥¦¦†è8€žAŸÂàéÓ§4�è8|ÍÔÔôÉ“'ööö ƒè8€@Ÿ/<<ÜÙÙyêÔ©·oß–””$:Ž5jT\\\UUÕ„ Š‹‹‰Žèôi Øîܹãîî>{öìK—.Q©T¢ã MMÍØØX2™<~üøüü|¢ã¾ ú4`—.]š3gÎÊ•+/\¸c�úJMM-**ŠN§�?þÍ›7DÇt ú4TÇ�_ºté–-[Ž;swô�²²òãÇ�µ´´ìííÓÓÓ‰Žè<»�äïï¿aØ “Crrr‘‘‘ÆÆÆvvvÏŸ?': 3èÓ@À°ÙìmÛ¶mß¾= `ëÖ­DÇÒÒÒ¡¡¡öööNNN>$:à_ OAÂf³7mÚtøðá?ÿüÓÛÛ›è8ÂC\\<$$dΜ9nnn·nÝ":ààÒ 0˜LæÊ•+/_¾âîîNtaC&“ÿüóO�6wîÜsçÎyzz�€ôi (ZZZ-ZêääDtá„aX`` ¸¸¸——WssóªU«ˆN€> ACCìY³###mllˆŽ#Ì0 ;xð ¢¢âš5kjjj¶lÙBt"DôiÀ磌«]]]ß¾}iaaAt‘àëë+##³qãÆŠŠ ¸¢bAŸü‚Éd~þüYYY¹ãÂ/_¾L�:µ¨¨èÉ“'£G�&*›Z¿~=�Fóòòª¯¯?rä†a¿[RR¢¦¦FT6D \ï øÅÏ?ÿ¬¥¥•��о¤´´tÒ¤Iååå±±±Ð¤ž‡‡Ç¥K—N�:µf͋վ|÷îÝC† yùò%�Ù›Í&:ˆÁ` :´©©IFF&66ÖØØ¸°°pòäÉ åáÇC† !: è ›;wîŒ3‚ƒƒ©Tê¡C‡¶nÝJ"‘¦N�zïÞ=¢Ó ü O¾ðý÷ß´µµQ(YYÙ‹/®]»VYYùþýûJJJD§uOž ˆ´k×.©óAˆa™L–––^·n]AA�‰‰ !Ùn÷îÝ)))óæÍ#“ÉJ癑˜Læ‹/BCC É€(€Ï§AÏX,ƒÁ`0uuu555¡ºº:üM°¼¼}óæM999—¹sç:;;S©TS„††^»víÙ³g:::+W®ôòò‚Ù¼BfffppðéÓ§kjjÆ�7wîÜ… vº'髬¬Ä§‡Љ‰-\¸píÚµ0X6juuu¿ýö›šš‰Drqq¹sçN[[Ñ¡:{õêÕ† ètº¸¸øŠ+òóó‰N¾),,ÌÚÚ!¤§§÷ûï¿ùò…èD�}þüùðáÃzzz!›ˆˆ¢ÀèÓB«¡¡áÀ�ÊÊÊÒÒÒ[·n-,,$:QêêêNŸ>­­­M¡P–-[Ýšß„……áú¹ººÆÄİX,¢u‡ÅbEGG»¸¸ „,,,ðOÐDЧ…Ó�7† &++ëççW^^Ntœ>hmm½pá‚®®®„„ÄÎ�;ëëë‰NØÙÙÙÎÎΆMŸ>¿‡INNvssÃ0lêÔ©999DÇ Ï O ›÷ïß;99aæááQRRBtœ~jmm=|ø0�N:t(¼"PKKËÎ�;ÅÄÄŒŒŒž>}Jtœþ{òä >êzçÎ�---DÇ  O •ëׯËËËÄÅÅ�… JJJ.\ˆaئM›šššˆŽ#ròòò,,,¤¥¥�9‚kh­­­RRR–––ð©  0¿·�`2™6l˜3gÎüùó“““mllˆNĪªª—/_>{ö¬µµuqq1щDÈ�;wLMM[[[SSS7nÜÈ£qV‰B¡øøø¤¤¤455™˜˜Àœä@P@Ÿ îîîþùçµk×Nœ8!))It"nZ²dÉ‹/š››­¬¬233‰Ž#Nœ81{öì ÄÇÇ�9’è8Ü4jÔ¨ÄÄÄyóæÍš5+((ˆè8ôÑoè§êêꬭ­ããã‰ÎÂC_¾|?~¼¼¼¼À]Ç$pöîÝ‹aØÞ½{‰Â[?þø#†aþþþD 0ω`kkk›1cFJJÊÓ§O…ì}ÏךššfΜ™žžþüùóáÇG8�={vÕªU'NœX³f ÑYxî�?þظqã¹sç–-[Ft¾ ú´`[·nÝÅ‹£££Û'ænuuu“&Mª­­MJJ¢ÓéDÇ6>tqqÙ±cÇ�?þHt–òÃ?øûûGDD888�€®ÁçÓìîÝ»'Ož ø&ýÛo¿a†aØ�!Cz\9==ÝÕÕUNNNVVÖÑÑñÙ³gýÞ¯ŒŒLXXXMMͦM›ú½Ð¥/_¾,[¶lΜ9{öìø½÷éˆB…‡‡ëêêr~uÛO?ýäîî¾lÙ²ªª*7¯}âôƒÁPUU]¶l�Œ��ÕÕÕ»_'!!ARRrþüùŸ>}ª¨¨Xµj…Byðà'û½sçB(44”“�€N-Z¤®®Nì< ½9¢òòòÜÜÜŒŒŒh4™Læ|§Ÿ?VSS[ºt)盀༷ Ú¾}ûùóç³²²<ý;f̃ññãÇo­Àb±ŒŒŒ¾|ù’ŸŸ�_…Îd2 rssÅÅÅû½ë… ¾|ùòõë×ܺa¢ˆKKK333»{÷®››�1z<¢B‹-222Úºu«¦¦fiii[[çû½yóæœ9sÒÓÓ�ŒŒ8ßÜÏq©¦¦æäÉ“›6mâóÏhŸ>}úæÍ›9sæ´#“É .,**ºwï'[Þ½{wvvöíÛ·¹ ýû÷�3fÚ´iDéÙÙ³gýüü¸;žÛÝÝÝÄÄdÿþý\Ü&Ü}Z ³X¬µkפÑÑÑ¡±cÇv\ˆÅÉ–õôôf̘qìØ1N6p¥¥¥7oÞôóóˆ[€óhz€­[·†„„”••ñbãpú´@ wrrâäÍtssó®]»ôôô¤¤¤ äææv÷î]&“‰‡Ïbfkk‹¯|ÿþ}|‰¢¢â×›ÊÊÊruu¥ÓéRRRvvv/ËÊÊBuº2H]]!”““Óïð¸¹sçÆÅÅUWWs¸pÿþ}*•Êáï�9¢xgúôé %22rö@ßý9賦¦&))©3gÎp²‘•+WÒéôÈÈȆ††ÒÒÒ­[·"„?~ܾ‚´´´��MÇ133SPPè¸ÄØØ˜N§ÛÙÙÅÅÅÕÖÖ&''‰‰‰ÅÄÄà+Lž<!”��Ðñ§rssB¦¦¦œäg³Ù ƒL&ß¾}›Ã퀅 :99q¸‘�9¢:RWWçÊudí/^ÌÅ Àð~Zð744Œ3†“�DEELž}útìØ±ºººüü|Ÿ.ßjHKKoذ!11±¾¾>%%eÉ’%bbb���øwI$ÒÙ³g¿|ù²|ùòÒÒÒÏŸ?¯_¿>777((HBB‚Ãü™™™ £ÓàlÐcÇŽ�‹‹ã|;pDñÔ“'OÌÍÍ`Gô ¡W›ƒ~Z±bE§!.}•žž¾fÍšQ£Fá£]Ç�Äb±ÚW¨ªªZ¹r¥ššš¤¤¤­­mrr²™™~Ìøúú-$<==ï§…Š’’RhhhPPPHHÈÈ‘#Ïž=ÛÖÖFt¨þHJJ²±±Y½zõüùó_¾|immMt"QD"‘¶mÛ–””„;vì†  Ñ¡ú£¢¢býúõcÇŽ­¬¬Œ��ÿþûïá:D Hˆ~¡x¢²²rãÆ� EGGçܹs---D'ê­çÏŸ»¸¸ „Æ�ŸžžNtÀf³Ù,ëüùó***ÒÒÒ[·n---%:Qo•––nÙ²EZZZUUÕÁÁ�D"éêê577 €Þ‚>-Ìòóó½¼¼¨Tª––Ö�ÊËˉNôM---×®]³³³CY[[GDD�tVWWwèÐ!UUU))©5kÖðù™ð´´´Õ«WKJJªªª:t¨¾¾žÍfççç{{{KHH¨¨¨ìÞ½[Ð?"ú´ð{÷îÝÆ�étº˜˜Ø¢E‹ŸNß-..ŽúGqq1…BÑ××7555119r¤¶¶ö°aÃ8™ù«´´4///777###--íÅ‹µµµÒÒÒ&Lpppppp066†Ën…F}}}lllTTTtttzz:‹ÅÒÐÐ055533Ó×××ÖÖÖÖÖ–••í÷ökkkóóóóòò233ÓÒÒRSS?~üH"‘ÆŒƒNãÇ�—’’ê߯[[[¯\¹ràÀ�ÌÌLWW×íÛ·ÃÀ? O ­àààï¾ûnÔ¨QýõW�3#æç秦¦âOéééøð …2lØ0555%%%%%%iii999 Ã$$$$%%™LfMM B¨®®®¾¾žÁ`0Œ²²²²²²wïÞáDKJJàÏצ¦¦ÆÆÆ0ë§Ð«¬¬¼s玟ŸŸ©©ivvvaa!>SŠŠŠÊ°aÔ””ñ¥¥¥ÅÄÄH$�NGUWW³X¬æææ††ƒQQQÁ`0ÊËËß¿_^^Ž"“Éššš¦¦¦¯_¿vttüñÇååå¹›Ífß»woÿþýÏŸ?·±±ñõõ�6m¼”„ƒ>-„***V®\yïÞ½ 6�F&“ñW� x/WTTÔÐÐ1b„ŽŽŽ––~<ïÚµëàÁƒIII†††\Ïçïïfhh¸eË–E‹Q(®ï€^‚>-l>|¸lÙ2 …rñâÅ &ðnG†]½zuÞ¼y¼ÛP{öìñ÷÷çQŵµµM˜0¡¶¶699YBB‚»ÈÈÈøí·ß®\¹‚_H��òâÅŽèÌG&<šššüüüœ��mllÒÓÓyÚ¤ø–ÔÔÔ_ýõÀ�¼kÒ! …réÒ¥>ìØ±ƒG»022 ÎÉÉqssÛ¾}»¦¦æž={*++y´;¾‰Ð«Ø×¼zõÊÈȈF£]¼xq`öˆºzõêÀì Šºº:]]ÝÉ“'ÌÚ.\À0,,,Œ×;*++Ç\ËÊÊz{{óz�´ƒ÷Ó�ÍfŽ;VZZúÅ‹K–,!:]ÿùÏ***Ξ=;0%xzz.\¸pùòåeee<Ý‘²²òž={>|øðóÏ?_¿~}øðážžž999<Ý)8èÓ‚­´´ÔÅÅeëÖ­~~~±±±Ã‡':]÷ïß?sæÌÉ“'544l§Ç�—’’Z¾|9›÷—ÚÈÊÊúøøœ>}:11qÔ¨QnnnBy‹nÀW O °7nŒ=º°°0!!aÏž=d2™èD@tUTT,_¾ÜÓÓs€/-¤Óé—.]ŠŒŒž~àíÚµËÄÄdÑ¢EMMM¼k¼=ãÝzÆŒøUâzßwÀŸ O ’¶¶6ÿñãÇ><==}úôéD'åççoݺuÛ¶m“&M"*à ÓêÞ­322LLLV¬X¡££ØÐÐ@H l¾ÞôÚ»wïlmm%$$fÐK÷ŒËlvkkë¸qãLLL𛛉Î2pôº÷îÝ;ooo)))%%¥Ý»wóÕ}?� ‚÷Ó‚!88ØÐаªª*11ÑÇÇfâ|bïÞ½ééé.\èÇô´\7`ôº§©©XXXøÝwß=ztذa>>>ÅÅÅF ú4¿«ªªZ´hѲe˼¼¼RRRŒŒŒˆNÀÿ˜©Çúd ‡iuOIIiÏž=ï߿߻wï�7ð!×ÙÙÙĦ‚ú4_‹ŠŠ=zt\\\TTT`` ¸¸8щøõõõ‹-š4iÒ† ˆÎò??L«{222ø�ë   ¤¤$}}}77·¤¤$¢sA}šO577ûùù999YYY¥§§ÛÙÙ�€Ù¼ysyyù€M=Ö{„ ÓꞘ˜˜§§gffæíÛ·ËÊÊ,--aÈ5è=èÓü(33sܸq'Nœ8qâĵk× Dt"þåþýûAAA<õXï8L«ø�뤤¤ö!צ¦¦ÁÁÁL&“èh€¯AŸæ/l6ûôéÓæææâââ©©©«W¯&:�µO=6þ|¢³t�ðaZÝÃßL§¦¦Ž=ÚËËKOO/00°¹¹™è\€OAŸæ#eeennnëׯ߸qcll¬ŽŽщèŠ+¨Têï¿ÿNt�î >üèÑ£aaaDgéþf:++ËÅÅÅ××WSSÓßß¿¾¾žè\€ï@Ÿæ·nÝ=ztfffLLÌþýû©T*щè>õXpp°¼¼<ÑYz€Óòòò"v˜V÷ðQrrræÍ›÷óÏ?ãw¹þüù3ѹ�>M¼ÆÆFww÷©S§fddØØØ�€®ñÃÔc}Â?ôº7tèP|Èõúõë�=ª©©éããSTTDt.À O,99y̘1ÁÁÁW®\ –‘‘!:]kkk[²d‰ŽŽÎ�?þHt–Þâ·aZÝSTTlr}ëÖ-üÆ\Dçƒ>M&“éïïokk;tèÐׯ_/X°€èDt‡¯¦ë=>¦Õ=|Èu^^^PPPJJÊèÑ£ÝÜ܈Î}šïß¿···ß³gÏO?ýôàÁuuu¢Ð>œz¬÷øs˜V÷ð!ׯ_¿¾}ûvEE…••Ü7SdAŸ&Àµk×LLL F||¼¯¯/‰U|�?§ë=>¦Õ |ÈuBB ¹eÐ!Tuuµ‡‡ÇüùóçÎ�‹2Mt"zÆ·S�õÿÓêþfúÅ‹†††^^^ººº���tzpúôÀyþü¹©©éÇïÝ»wêÔ))))¢Ð3>Ÿz¬÷b˜V÷ðkN³³³§M›æçç‡⪮®&:à-èÓ¡­­mÏž=&L066~ýúµ‹‹ щèþŸz¬Oe˜V÷´µµñA\k×® :t¨��Oii)ѹ¯@Ÿæ¹¬¬,KKË:tèæÍ›ŠŠŠD' ·bê±Þ¬aZÝSQQÙ³gχ~úé§��MMÍ5kÖ|øð�è\€û OóVppðرcÉdrzzº��Ñqèšz¬÷n˜V÷h4š��Oaaá‘#Gîß¿�¹ÎÌÌ$:à&èÓ¼RQQ1}út//¯ 6ÄÅÅéêê�€>(((¬©ÇzO‡iuO\\|õêÕyyygΜÁoïáææOt.ÀЧy"22ÒØØ8###::zÿþý‚5/mmm‹/¬©Çz�B¡üõ×_‚8L«{T*r}çÎ�ÏŸ?[[[Ã�ká}šËššš|||œ��mmm_¼x1a¢Ðg:õXïiii ô0­n`æææöüùs|ÈõŒ3ð«ÄÛÚÚˆŽú ú47½~ýÚÂÂâÂ… /^ ¦Oõ€èè©ÇzO†iu¯}ȵ±±ñŠ+ð!×���Dç}}š;Ølv`` ™™™¢¢â«W¯/^Lt"úCЧëá¦Õ=ccãàààœœ77·íÛ·ãC®«ªªˆÎúâtÀyzz>{ölÇŽ?üð™L&:O=zôôéÓí_æä䨩©ÉÊÊâ_jjj††† ôÓëׯkjj¬­­Û—¬Y³&$$$##CÐg5é¥gÏžMœ8ñèÑ£ëÖ­k_«¥¥5dȃñByyùñãÇ™LæòåË}}}Lt(Ð lÀ™k×® 4HOO/55•è,¼õË/¿ts éëëô™µµ5†a›7onlld³Ù†ýý÷ßDçP?üðƒ„„DFF›Ínhhøî»ï0 [²d ѹx¥¦¦& `ðàÁâââ999D'=€>ÝÕÕÕ«W¯FyxxÔÕÕ‡çòóó¿5½3•J=pàÑAßTWWãç~(Šžž^LLŒªªêÒ¥K‰Î5ÐZ[[­¬¬F��””¤««K¡PBrrrL&“èh<ÔÔÔtáÂ…#F�H¤iÓ¦¥¤¤�|ôé~Š��×ÖÖVVV¾{÷.ÑYŽ™™Y—­ðwïÞ�ôÍÍ›7Û«I¡PÈdò�!C¾|ùBt.äååijjЉ‰áM—��@t.žc2™wïÞ533C9::ÆÅÅ�t®#ë3|²n[[[�ôôt777¢ OOϯ?}'‘H–––šššD$ý÷àÁ*•Šÿ¿­­�Éd~úôÉÙÙ¹  €Ø`¬¼¼|Æ >|hiii¼$&&&|C¶¾†ß73%%åáÇ���¶¶¶0äšýB�Oåææ}½¼  ÀÆÆFRR2 €Åb |0b•••}}·l …rüøq¢£�>SSSûú �J¥JII�:uŠètäÁƒŠŠŠí¯W:244$:Ý@‹���6m†a†††.\hmm%:`³á¼w—ššš B×®]ë¸üÂ… 222†††ø%'¢ÉÎήÓ[j2™\VVFt.Ð7=NýôéS¢3ò܃0 ûÖU†}úô‰èŒÈÈÈððð P(šššõõõD'upÞ» ÿýï³³³1 [±bEqq1BˆÁ`Ìš5kÙ²e^^^)))Â=ÿC÷<<<ØΉ‘ÉdGGGeee#�~èxÒ»#‰D"‘vîÜÙq°–°²²²š3g›Íî²U“H¤ˆˆˆ�OE8CCÃàààÜÜÜéÓ§ïØ±r]YYIt.Fô ¾ÙþGK¥Rmmm###ÕÕÕ‡úøñc¢Ó¯ºººã\’$éâÅ‹D‡}æààðõGT*UUU5&&†èt*$$DVVöëW-d2yÖ¬YD§#XyyùîÝ»åååeee½½½?~üHt"QóœüKee¥¾¾~EE“ÉÄ—�H$;;»Aƒ�|X´hQ||<‹Åê¸\JJª²²RX'9ï½ÚÚÚsçÎâáá�’‘‘ijj": è3---„‰DÂ0lãÆ�ÍÍÍD'â# 7nÄ0 ÿü~øðáD'âSø .„�©©é… ÚÚÚÚÚÚ455ñ“mT*UQQñÍ›7DÇ|úùtccceeå—/_«««ñ…íÊÈÈàg§edd$$$äååååå9ùˆ´¥¥ÅÜÜ<33³›[´b†ß!®ß{P]ÖâñãÇ¿þú«��Ý÷ßÏÝZ€npåïâÝ»wÇÇ0LAAáòåË“'Oȇ (îÝ»·téÒÊÊJ6›�ŸŸ?|øðN+ ðsßzþü¹¿¿hh¨¾¾¾££ã‘#GÚ{ …B¡ÑhOž<=z4O3}-ˆìÓ555oß¾-(((**úøñcaaaQQQEEþëîëÖ(м¼¼¢¢â�!C† 2tèÐaÆihhèééõxß??¿ß~û­ýÚ±ŽÛd2™†�;vÖ¬Y>>>ݼçhüS ÀëZ^¸pÁÎÎîêÕ«JJJ¼x¡¼¼|É’%>ôòò9r$ü]tãÍ›7þþþ<`0¯Å£P(²²²Ož<á|,«(?G \Ÿf³ÙYYY‰‰‰¯^½zýúõÛ·o‹ŠŠB eðàÁC‡:t¨†††ŠŠÊ Aƒäååñ%%%åääð)t:?U[[‹¿ñmll¬¯¯Ç_Iáÿ2Œ¢¼ÿ¾¡¡ÿA}}}sssSSÓŽí666vÒ¤IídžaT*µ¥¥…N§Ož<ÙÍÍÍÍÍM^^~`~Kƒok!‚¾………øóÔ¢“oÕ‚L&ã#3áï¢..._/§P(222111}: ÏQñ¶O777ÇÅÅÅÄÄ$&&&%%UWWKHHŒ=Z__ÿEèëë6Œw7l®¨¨ÈÌÌ|ûö-^錌 ƒA¥R���---mllÌÍÍmmmËÊʨTj[[†afff3f̘:uª‰‰É·&*Dü_‹É“'+**òhï|jÁ? Übcc“””ÔåG‡ ERR2**ÊÜܼ›-@-¾…'}:777<<<222&&¦¡¡aĈãÆ�³°°°´´3fL—³ ˜üüüĤ¦¦â·®“””œ8q¢§§§³³³�½u Z°X,SSS''§)S¦ØØØð(P þµà®ÄÄÄqãÆu³™L–””|ôè‘¥¥e§oA-zÆÅkÒ lllB222ŽŽŽü|»Ãººº���åË—ã·uSPPððð¸{÷®Ì>/ˆµxøð¡¯¯/Ô‚pP þ!(µX°`B¨ÓÐóNÈd²ŒŒLûÝB¡½Ç…>]__æÌü„†²²òÚµk£££ÛÚÚ8ßò@ÊÎÎþùçŸñ‹ÔÔÔvìØQXXHt¨>ƒZð¨ÿ€ZðÚÓ§Oøá‡åË—O™2EWW—N§··g ÃÄÅÅ%$$ð..%%µcǨEŸpÔ§srr¼½½åääÄÅÅ—,YòèÑ#�ûu-33sçÎ�jjjd2yÚ´iqÿJ¨ÿ€Zð¨Q âââ®^½°uëÖ3f¨©©‘H$ …µè“~öéüüüÕ«WS(uuõÝ»w———s7áÚÚÚîÞ½ëèèˆa˜‘‘QHHþ%à üjÁ? üjÁ¡>÷é÷ïß/\¸�D"éëëÿõ×_Bð‚¨{iii3gÎÄ0l̘1< :ο@-øÔ‚@-øÔ‚+úЧ›››ýõWiii]]Ý«W¯âWJ‹ˆôôô3f „æÌ™STTDt¨Ô‚/@-øÔ‚p½½íÓÉÉÉzzzRRR¿üò‹ÈΡ££#--}ìØ1c@-ØP ~µàP þÁÅZôܧñÛ½‰‰‰9::òóEó£±±ñ‡~ “ÉîîîøÜ¿ jÑÔ‚@-øÔ‚p«=ô鯯ÆÙ³gS(”_~ùE¤N\tïñãǃÖÒÒÊḬ̀�B-ºµàP þµàœ×¢»>]SScgg'//ÿôéÓþm]ˆ•——ÛÚÚ***¦¤¤ >6Õƒ IDATÀî Ý€Zð¨ÿ€ZðkñÍ>][[kaa¡ªªúòåKâ ³úúzggg�Ö>Ã�@-zµàP þµàœÔ¢ë>Íb±fÍš¥¬¬œ““Ãq¼þ8xð >—�ººz�+¿xñÂÅÅ…N§ËÈÈ888ÄÅÅuü®´´ô×3ØIJJ:tˆÃqÍÍÍ®®®ªªª?~äd;ݦZt”““ƒ²´´äVN¨Å×ÂÂÂFŒA&“»_ jÑ\¬�™™I¡PŽ=Úï(ÜÒãï�Éd¨©©544àKÚÚÚFŽ©¡¡Ññ·Ðéo÷üùs|²Ù––sæääHKKïÛ·�Ãí|MøjѾ²ººº‰‰ BhëÖ­\Ì µÀ-\¸pß¾}­­­êêêÝ÷i¨'¸U xŽâ·j±cÇ„ÐéÓ§;-okk›:u*‡}šÝ¯Ztѧ���MLLøáj½ï�?Fmܸ±ãÂ={ö „®_¿Þ¾¤Ë¿�ÏŸ?ã/Ž***8�úÓO?ÉÈÈ|úô‰óMu$|µÀEDD 6,99!¤¢¢ÂÝÎ@-Ølvû«¥û4Ô‚ܪRœ¤¤äÒ¥Kÿþûo6›Í• "á­Å—/_BCC—.]ŠZ¾|9Bèܹs>ÆŽ ½µàŸZtÏQDÕâéÓ§!üÊÞés-:½¿ÖÐÐØ½{7‡oêÙlöÊ•+étzdddCCCiiéÖ­[B�?n_AZZÚÆÆ¦ã�˜™™)((t\bllL§Óíììâââjkk“““�ŒŒÄÄÄbbbð&OžŒêt™{nn.BÈÔÔ´} ~N©“‘#GÞ¸qƒóGÚîÕ«W_‡á„PÖ‚Íf=zÔÎÎÿEE•J¥P(eeeœ?Òv"^‹Žº?ï µà“ZÀsŸÔBMM !”˜˜Èùƒê^Ÿjñ¯>]YY‰ŠŒŒä<„–––µµuÇ%ºººýø½#„âããÛ—ddd „Œ��ñ/»ì ø»·Ž0túì§µµµ  `÷î݆¹»»s~�F;�væÌ®lJXkÁf³MMMƒƒƒÛ¿œ5kBè·ß~ëߣûQ®EGÝ÷i¨EÇ%Öž£ø¤xŸNJJê×ãè›Þ×â_ç½?~üˆ¾:uÙ?ÎÎÎÏŸ?_½zuBB~ú";;»§G$$$,--Û¿444!ôæÍ›¤¤¤~¤úQ®E/A-ø§ßÏQ}Ây-&NœˆÂ߈óZïkñ¯>�¿åçäe`; Ã<<<=zTUUuûöm6›íîî~øðáÿí˜Djiiéø#]&Æ…Žðß8þÛ·³³C¥¦¦v\ÿÒÁÁ¡Ç�l6!ÔÐÐлÇÔ³ââbüÊ)Î e-Z[[ÿúë¯gÏžu:«³iÓ&Äí·q¢\‹Þ€ZðO-ºÏQ¸«Åš5k(Êõë×»üî¶mÛH$~Í,çúP‹Ž¢MMM†]»v­_'Ûÿ…N§¿}û¶ýˆ† ÃìííÛ—èêê<¸ýË’’*•Úåç éééíK:}ÞÀd2õõõÜØØˆ/ikk5j”††Fûö7Æ&²ÙìÅ‹#îÍíÐÒÒ"&&vùòe®lM(kqãÆ ==½¯â—TÐéôöÑ�ñZtô­Ïá üS 6Õ¢óõÞãÆ�[¾|9ç!ètúĉ_¾|ÙÔÔTVV†Ïw±wïÞö6lØ€:zôhmmm^^Þ¼yóÔÕÕ¿þ½KKKÛÚÚ&$$ÔÕÕuyý^||¼„„Ä‚ JJJ þjèþýû·óõ5ï޽ïÑPWWçÖÀÿ{÷îaöþý{®l�-Œµ˜6mÚ�º iaa�ºtéç�— µèà[ÏGP þ©ž£ø©l6ÛÏÏ�J¥úúúfgg777üøñÌ™3jjj¶¶¶uuuœ?^vkѹO>|X^^žói`ÓÓÓ׬Y3jÔ(|<ܸqã‚‚‚X,Vû UUU+W®TSS“””´µµMNN633ÃßâûúúvœW=))ÉÎÎNFFFRRrâĉ_ßÚ!--mêÔ©4MFFÆÞÞ¾Çûp`&++kll¼mÛ6.AñôôìtE"‡„©/—èt¿‡wïÞu¬ŽŠŠ ‡�— µ`³CCC¿>yĆZüƒj�ƒç(þ©E»¤¤$ *•*++;nܸÀÀÀææfi»>Õ¢sŸþô铤¤äï¿ÿέ4""77W\\¼Ëz÷Ô¢ üjÁ? ü£¯µèb~ï�;wÊÉÉqeFYÑ1}út}}}îÎ�̆Zô Ô‚@-øÔ‚ôµ]ôéÚÚZuuuww÷ާ@7.^¼ˆ¸4Þ¿¨E_A-øÔ‚@-øG?jÑõý§Ÿ={&..þã�?r)˜0KKK“’’Ú¶m�¶µè=¨ÿ€Zð¨ÿè_-ºîÓl6ûäÉ“$‰»fŸÌÌÌÁƒO™2¥­­�w{�ZôÔ‚@-øÔ‚ô»dü ø¯�;ð-[¶HKK[[[w¹ŽˆKIIqtt1bÄ­[·ú}ëºÞ€ZôjÁ? üjÁ?8ªE÷m<00ð�7r~å½�¹~ýº¬¬ìÔ©Sëëëf�P‹o�Zð¨ÿ€ZðkÑCŸf³ÙW¯^¥ÑhfffyyyýØ�ðillüî»ïBëÖ­ãâpºÞ€ZtµàP þµà\©EÏ}šÍfçä䘚šÒh´#GŽðôCþkhhH§ÓCBB µhµàP þµàܪE¯ú4›ÍnjjÚ±c‡˜˜Ø˜1c¾ž»_”––zzzb6eÊ”üü|“@- üjÁ? üƒ»µèmŸÆåææ:;;#„æNÚü€Á`ìÞ½[NNnðàÁ.\ :ÎÿƒZ@-ˆµàP þÁ‹Zô­Oãîܹcff†ß­6>>ž+9øSqq±¯¯¯ŒŒŒ’’Ò¾}û¸5;A-øÔ‚@-øÔ‚sýéÓl6›Åb…††š››#„LLLNŸ>͇ÇG¿±X¬‡Ξ=›J¥*++ûûû×ÖÖê› üjÁ? üjÁ¡~öévÏŸ?÷ðð��� Óé+V¬xðà×ç�H¯_¿Þµk׈#B¶¶¶—.] P þµàP þµèNû4ŽÁ`:thìØ±!EEÅÕ«W‡…… Ê+&&“™œœüã�? „ÔÕÕ7mÚ”‘‘At®~‚Zð¨ÿ€Zð¨E_q§O·ËÏÏß·oŸ©©)†aâââvvvûöíKJJjiiáîŽ8———wæÌ™ùóç+**â¿î�7ÆÆÆ2™L¢£qÔ‚@-øÔ‚@-z c³Ùý˜­G¥¥¥>ŒŒŒ|ôèQii©¤¤¤©©é¸qã,--MMMµ´´H$/öÛ�ŠŠŠ—/_&þ£¼¼üÿÚ»Ó ¦®¿à7� �`(;ˆ Ê„V$(`Å"ŠvÀY¬vTÐq:hQ)�ÖV[q)¨uA;Æâ0Újµ ­tD –­” È&"ûÂäÿâ>塀ȕ~?/˜äääÜ9/¾9÷ÞÜËd2—.]êææ¶bÅ ++«I®gÒ`.¨sA˜ êÀ\Œl¢rºŸT*ÍÏÏOKKKMMMMMÍËËëííe2™æææ ,˜3gŽ‘‘ѬY³tttd²ÑÆÆÆŠŠŠŠŠŠ²²²üüüüüü¼¼¼††‚ /^¼hÑ";;;eee™lñ­€¹ Ìu`.¨s1¬ ÏéAÚÚÚž>}š››[PP�———ŸŸ_QQÑÓÓCƒÁÐÓÓÓÑÑÑÐÐPWW'ÿªªª²X,ƒADÿƒææf²l¡P(‘Hššš„BaÓ?ž?ÞÑÑAnnæÌ™æææóçÏ·°°˜?¾¥¥¥®®îdþ¿T†¹ Ìu`.¨sAšìœª§§§ººúÅ‹åååuuuB¡�ü…Ba{{{GG‡X,&¢µµ•œ!‡C£Ñh4‡Ãa0ä ‘S¥¡¡adddhhhhh8kÖ¬ ½EÌÔƒ¹ Ìu`.¨ã¿9òÏi™£Ñhqqq6l�w!ã5Ùç`ô�ÓÔ…œ .ä4u!§¨ 9 @]ÈiêBNPr€º�ÓÔ…œ .ä4u!§¨ 9 @]ÈiêBNPr€º�ÓÔ…œ .ä4u!§¨ 9 @]ÈiêBNPr€º�ÓÔ…œ .ä4u!§¨ 9 @]ÈiêBNPM*•Ê»€ñ:sæÌ¥K—úŸ>{öLWWWMM�|jll|çÎ9•6.Šò.@Z[[sss¶”——÷?îëë›ôŠdû½`*ðññ¡ÑhþÄ`0¶lÙ2¹åÈ ö{ÀaooŸ••54×h4Zii©±±±<Š/¬§`ŠØ¼y³‚‚ F:�¾hÑ¢·4¤ ä4L>>>C�CÓéôÍ›7Ë¥™@NÀ¡¥¥åìì}:ûL&“Åb1 ‚ èt:›Í&Çììììêê" …žžrýÝÜÜL>hll¬¬¬ìÿP__OvÖÖÖ655555511±´´är¹FFFòøT�Ó '­­­)))iii� ;;»¬¬Œ MMMsss2 É¿sçÎíÏ݉&‹ËËË‹‹‹‹ŠŠÈï EEEeeeR©T]]�Ëår¹\;;»%K–LZl#§`ò466>xð 99™Ïç ‚ÞÞÞ¹sçÚÚÚr¹\.—k`` ïkiiÉÉÉÉÎÎ&¿O‰Dbdd´téR�çêêjff6q[GNÀ„ËËËûõ×_>|Ø××gffæääÄãñ\\\äµ?ù�I$’œœœÄÄD>ŸŸ’’" gÏžíææ¶|ùò•+W²X,Ùn9 %##ãǼ~ýzUU•ŽŽŽ‡‡‡‡‡ÇòåË'm?öDëééIKKKHHHHHL&såÊ•~~~Ó¦M“É&�Ó cÏŸ?�‰‰‰��-,,455õõõõôô\¸p!�F“wi¨ªª*>>>...))IMMÍÛÛ; ÀÙÙyœÃ"§@fø|~TTÔÍ›7Ùl6T<ojÇóPUUUׯ_¿~ýzJJŠ™™ÙÎ�;·mÛ¦ªªúf£!§`¼z{{¯^½‘——çää´gÏ//¯�÷­úoÊÊÊ:{öìµk×TTTvìØ¢©©9ÖA�Óðæ¤Ré�7ÂÃË‹‹ýýý÷îÝkkk+¥¾¾>:::22²««ëã�?ÓET�Óð†ÒÓÓwîÜ™��½qãÆÏ?ÿÜÔÔTÞQW[[[ddä‰'�98ÊÃoÇÕM€RÚÛÛƒƒƒ9Îßÿ‹�‹Å:|øpiié–-[öìÙãìì\PP0š7"§`lÒÓÓ---cbb¢££­­­å]Ñ[C]]="""##£££ƒËå~ûí·¯} rÆ &&ÆÙÙÙÂÂâéÓ§[·ný¯�Ë-\.7555<<<$$dóæÍýW ŽOÀh…††?~üÀ�G�}[î Ie÷îÝÛ´i“©©éÝ»w_u*8>e•O?ýôĉ111_ýµ¼Búĉ4�F£�ò2à óæÍùb[¶l™={6“ÉÔÐа´´ôöö>þ|II‰Œª~%ww÷ôôôºº:�ÖÖÖaû §àõNž<ùÕW_]¾|Ùßß_Že„„„H¥R›×ö,))Y³fÍÁƒkkk_Õ§¯¯oÿþýŽŽŽZZZwïÞmnnÎÏÏ?}útKKË®]»LLLÈ»jN(SSÓÄÄÄŠŠŠµk׊ÅâazHF”‘‘¡¨¨xâÄ yòlllôõõGî³iÓ¦cÇŽI$}}}…aû:tˆ ˆK—. jïééY¹r%A‰D6¿Î“'OÔÔÔ†¾„ãÓ0’îîn{{û™3g&&&Rä¬1.—ÛÐÐðòåËútvv2™L‚ jjj†®Œ ,X`kk›™™9ôíþù§£££D"™´«ª�?~ïÞ½©©©vvvÛ±ßFrùòåÒÒÒ+W®P$¤G‰ é\ºt©¯¯oýúõþºxñb©T:™—> âñxû÷ïÔŽœ€‘\¾|Ù××רØX&£‰Åâððpsss OOÏÛ·o÷ööñå—_’çˆ999‘�ïÝ»G¶Ì˜1cèP«V­b³Ù***®®®)))cªäÑ£GAŒòÇß===qqqnnn:::L&ÓÊÊ*22²¯¯oL[�F MJJ***ú× “³çÞFýõA©©©²pÛ¶ml6û÷ßïè訩© !âÁƒýTUUy<ÞÀ·ØÙÙijjl±±±a³Ù®®®|>¿µµ5##ÃÚÚZII)))iè_u|ZWW— ˆ´´´Ñ”}ç΂ Ž=ÚÔÔT__E§ÓÉ“Úd¨··wÖ¬Y؈õ4¼Òǵ´´-Z$«ÿøã� ¸¹¹1™Lmm툈ˆyóæ½Á8"‘èèÑ£<�ÅbÙÛÛ_½zµ»»{ß¾}cgô;ó]\\<¨®®>cÆŒ={öøúúFFF¶´´Œu‹# Óé«V­zøðá¿e¸˜bÊÊÊÌÌÌd8 »»ûãÇ�wìØ‘ššJîî.,,tqqë8ÊÊÊ¿=XYYééé ‚êêêQŽ §§GDCCÃh:¯^½úÁƒ[lll$I^^Þ¨K33³ÒÒÒ�-Èix¥þ³¦eåܹs111¥¥¥ï½÷ÞôéÓÝÝÝoÞ¼ùãhjjZ kiiQWW7Êœ�� ‚ÈÉÉMg‘Hnee¥®®N2'Oøêèè[ݯ£¢¢ÒÙÙ9°9 ¯Äápšššd8 �F HLLlnn¾uë–T*õòò:uêT:�ÞÝÝ=ð-ÍÍÍCljDƒZÈ„&Óz4oܸ1ì« Óéý·´òôôM„TÖ¿mnhhPWWØ‚œ€W²´´ÌËËùFcÂápÈðc0nnn·nÝ¢Ñhñññýtuu+++ûŸÖÔÔ¼xñbè8mmm� ÿé“'OªªªlllȳÃFcÞ¼yŸ}öYffæ÷ß?è¥Â‹/nذÁÜÜœ ˆÞÞÞ””�½{÷Μ9“\ÇZõÊJVV–¥¥åÀä4¼’»»{OOÏ›íš~•   œœ±X\WWwüøq©TºlÙ²þWW¬XQUUuöìÙ¶¶¶’’’}ûö »DVUUݽ{wZZZ{{{ff¦¿¿¿’’Rddä˜* }öìYwwweeå•+W\]]­­­¯\¹BvSPPpqq©©©‰ˆˆhhhèìì|ðàÁ… Æó! K$ÅÇǯY³æ_­²=§¦///WWWY�–��haaAþ~ÚÁÁ!::šÜ“LjnnÞ¶m›®®.“ÉtrrÊÈÈè¿>×'Ÿ|A>Ö××OOOwuue±XL&ÓÙÙ™ÏçÜùKªA¢££‡–”žž`hhÈ`0ÔÔÔ"##ÅbñÀ>õõõ���dmmí-[¶„††’cÚÙÙÉêÃ9{ö,‹Åjii؈ë†ÀH’’’\]]oݺµvíZy×2•566ZZZ®[·îÌ™3Û‘Óð}ôѽ{÷rss544ä]Ë”åçç÷ðáÃÜÜ\‡3°ǧà5N�:E§Ó}||†¿ñ"Œ[TTÔµk×¢££…4�œ€×âp8wïÞÍÌÌܸqã$Ü’ù¿æêÕ«ÁÁÁß|ó y3ÍA�ÓðzVVVwîܹÿþúõë[[[å]ÎÔqîܹ­[·:thè�²H8> £•œœ¼nݺ™3gÞºuËÄÄDÞå¼ÝÄbñ®]»~øá‡/¾øâðáïê†õ4ŒÖ’%K233§M›öî»ïÆÄÄÈ»œ·XNN�Çûé§Ÿ~ùå—Bš@NÀ˜òù|??¿­[·¾ÿþûeeeò®è-ÓÕÕfoo?mÚ´ôôôÕ«W�Ü9 cÃd2Ïœ9Ãçó+++­¬¬Â†½7 Ò××wíÚ5++«¨¨¨S§N%''�æžžÈix‹/ÎÊÊ ¿páœ9sŽ;ÖÞÞ.ëöíÛ¶¶¶ŽŽŽyyy»wï¦ÓGÁ8� Æ¥­­íܹsÇŽ#âÃ? 666–wQT!‹ãââNž<ùäÉ“U«V}ùå—666c9 2ÐÐÐpñâÅï¾û®¶¶öƒ> Z¶lÙ(—ŒSRQQÑ•+W¢££ÛÛÛ7mÚ´oß>.—ûã §@f$ÉÏ?ÿõøñc===??¿… Ê»®ÉS]]wíÚµôôt}}ý�;wnß¾}ôwÅ 9 ²WXX[\\lff¶fÍ�Ç`0ä]Ú„(((ˆ�����ô葪ªª———¯¯ï²eËÆ92r&PZZÚõë×ããã Øl¶›››»»»“““™™™¼K¯ÆÆÆ”””û÷ï'$$”––jjj®X±ÂËËkõêÕÊÊÊ²Ú r&Cii)¹âLNNîèèÐÖÖæñxK–,qtt´¶¶–a°Mœ¾¾¾’’’ÔÔT>ŸÏçóóóói4šµµµ‡‡‡‡‡‡ƒƒÃøWÏC!§`RI$’ÌÌÌ”””äää”””ÆÆFEEE333î?ÌÍÍõõõi4š¼+%š››‹ŠŠAvvvvv¶@ hkk›6mš½½½“““““�ÇSWWŸÐ�Ó 7R©´¨¨({€êêj‚ ˜L¦É?æÌ™£¯¯¯§§§«««­­=kÖ¦¦¦êêꪪªêêêçÏŸ744Áb±¬­­mlllmm¹\®••Õd®þ‘Ó@!õõõ………ÅÅÅýaYVV& ÉW´´´´µµ9{‹¥¨¨¨¦¦Fvc³ÙäOÂZ[[ÉqvuuuvvŠÅb‘HÔÒÒ"‰„B¡H$jll¬ªªêêê"ߨ¬¬ldddbbbjjjjjJ~Q˜={¶`†œªëìì,VÀrIDAT$»•••555µµµÍÍÍ¢ÚÛÛÅbqGGÙ¿?×UUU•””‚`0,‹Á`�¹ÎápȤ×ÐÐÐ×××ÕÕÕÓÓÓÓÓ›è�Øo9 @]ÿÝ+ÅPr€º�ÓÔ…œ ®ÿÌÝ å¯QiIEND®B`‚anytree-2.13.0/docs/static/dotexporter1.png000066400000000000000000000541171500063244000206330ustar00rootroot00000000000000‰PNG  IHDR›ï „bKGDÿÿÿ ½§“ IDATxœìÝw@wÿðo a«­¨€¢ ¢2D1 E78jŪOÕ–ºj•¶ÖQ­�¸Å=Š¢8¥8P@¦ˆE+�(­( ¢‚ Ù÷ûãòË“‡r—äýúË»|ïò¾ûÉÇ»ï]8E D‰él�Â@…€—é2¬¼¼<<<¼¦¦†é ÒЭ[·aÆ1� }¡0h½ðððÉ“'3�BJ¸\nUUÓ)Ú.¥´^uu5!„RAAAôÆÈ7FB(Œ„P¡0Ba „Â@…€ #!FB(Œ„P¡0Ba „Â@±¤¦¦º»»ëèèhii¹¸¸ÄÅÅ1�€EP(�¤¤${{{--­G�egg›˜˜Œ1âÚµkLç` F2OSSÓÁÁ¡ÙfµµµóæÍÓÑÑ9zô¨¾¾¾žžÞ¾}ûzôèáíí]QQ!…œì‡Â@QÄÄĤ¥¥M™2…ÇãÑs”••§OŸž““sùòef³° #EqóæMBÈ AƒÄgÒ“‘‘‘Ìd`FíëÒ¥Kœÿ÷øñc//¯N�:Ñ“„�7oÞ|óÍ7=zôèСCÇŽG�}ëÖ-ñ54Ñ`Û¶m§´´4..Ž^'—Ëm,Izz:!ÄÈÈH|¦¡¡!!äÉ“'m¾á²…@ûš0aEQãÇ�'„øøø,Z´('''11QYY™’ŸŸ?xðàÀÀ@ÿ‚‚‚¤¤$uuuggçC‡Ñ‹7ÝÀ××—¢( �¡C‡REQTuuµè­�œœ:uꔘ˜HOB444ÄãijjBÞ½{'�}Àz�þçÚÜÊ•+GŒA±±±¡+˜¹sçfggŸ>}zìØ±„mmíÀÀ@“¯¿þÚÃãk×®«W¯nºAoW[[KWKM´¡_åp8m·•2 gŒ¤gÈ�!uæ\¼x‘âîî.š£ªªêììüáÇ«W¯JÒ  QQQoß¾µ³³£'utt!¥¥¥âmèIú%@a =u.cUTT©©©iii‰Ï§Ïåçç7Û EïnnnNyþü¹øÌ/^BLMM[´*y…€1ªªª|>¿¼¼¼¸¸X|þË—/ !ݺuk¶=)á…0�@@IIIŸIO:;;·~3ä #&Mœ8‘råÊÑœŠŠŠÈÈH�7jÔ(IBÔÕÕ+++é›™™4ø^ŽŽŽ}úô .//§çÔÔÔœ9sÆØØXüR€"CaÀ¤M›6uïÞ}Ù²e—/_...~òäÉŒ3òòòüýýéëeÍ6 „ 8ðÉ“'999 YYYÆ £ç×¹+MIIéðáÃoß¾�;wn~~þ›7o/^œ‘‘qðàA555F6€mP´¯ÄÄD‡Báñxu.{uëÖ-99yúôé_ýu§N�† RZZzãÆ�ùóçKØ€²cÇKKËÞ½{O�:Õßß¿wïÞôüêêê:w¥ÙÚÚÆÇÇ™™™}öÙgQQQ¢3OÀiúV^hÂÙ³g§N�ªDг¥ àpÆ@…€ #!FB(Œ„P¡0Ba „Â@…€ #!FB(Œ„P¡0Ba „Â@ˆËt™wîÜ9¦#´»ÄÄD¦#H #€ÖÓ××çr¹^^^L‘###¦#´;\Jh½aÆUUUQÿ«¨¨hìØ±ªªªÇ�§X&>>^IIéßÿþw+–ÍÉÉaz´;e???¦3È�ÌÌLWW×'Ož„……�7Žé8uóx¼+V 0ÀÌÌŒé8¬Ã¡(Šé r"&&fÊ”)†††!!!Ÿ|ò ÓqõÕW_�:u*66ÖÊÊŠé,ì‚Kim# ÀÅÅÅÉÉ)..ŽÍU!dçÎ�666cÆŒyþü9ÓYØgŒ>VeeåÂ… �?¾qãÆ•+W2G"ïß¿···WUU�‰‰ÑÐÐ`:[ 0ø(“&Mºÿ~`` »»;ÓqZ ;;ÛÆÆfذaçÎ�SRÂÉcBp) àc<}úÔÁÁ!;;;::Z¶ª"BH÷îÝ/\¸påÊ•ï¿ÿžé,l� •"##ܱcÇ»wïöïߟé8­áààpìØ±-[¶0�€P´Æ¡C‡F�=räÈ›7ovíÚ•é8­7mÚ´5kÖ,Y²$22’é,ÌÃ#€–¡(êÇüé§ŸV¬X±iÓ&‡Ãt¢�EQÔÌ™3#""LMM™ŽÀ$F-PRR2cÆŒëׯ=ztÚ´iLÇi3ååå�àÕ«W‰‰‰�;wf:cPH*??ìØ±999!!!¶¶¶LÇic¶¶¶ׯ_WUUe:30Æ@"OŸ>6lXaaall¬üUE„==½ÐÐÐ,\¸�é,ŒAaм„„;;;==½„„„^½z1§½ôéÓ'((èÔ©S›7of: 3P4ãÂ… ÎÎÎÆ »yó¦Ü�¿5jÔÞ½{׬YsæÌ¦³0…@Süýý===çÏŸÌãñ˜Ž# óçÏ_¼xñ—_~™˜˜ÈtiÃàk€†ÕÖÖ.[¶lÏž=Û¶m[¾|9Óq¤ª¦¦fâĉÉÉÉIII,ÿA\€¶… •••³gϾxñâ©S§¦L™Ât;88ÔÔÔÄÅÅñù|¦ãH #€ºÊÊʦL™sñâEWWW¦ã0æŸþ±±±8p`hh(—Ëe:€4`ŒÀÿ(,,9rdRRÒ�7¹*"„|úé§W®\‰ŽŽöõõe: €” 0ø¯üüü#FdeeEEEÉåÊZÊÚÚúøñã»víÚ³gÓY¤AÙÏÏ�é ¬���íääT[[�Ÿ éÓ§�²²òªU«ˆÝrcŒ!äÁƒ£F�244üã�?äþaE­@E�‹‹ëׯÓYÚ #r÷îÝQ£FYZZ†„„hkk3‡�ªªªF�•���˜˜ØµkW¦ã´F èèªhÈ�!.\P�G8¶Î›7oìììø|~tt´ºº:ÓqÚ_ƒB»}û¶“““��ÝÅ‹å¦*Ú¶m‡Ãáp8FFFÍ6NMMuww×ÑÑÑÒÒrqq‰‹‹k¬e§N�ÂÂÂ233çÌ™£°ÿ¡jѾ%„„‡‡›ššâa2…(®èèè1cƸ¹¹]¼xQMM�é8mÆ××—¢(++«f[&%%ÙÛÛkii=zô(;;ÛÄÄdĈ×®]k¬½™™Ù¥K—BBBö¦ É÷mffæ¸qãV¯^ýòåK)€¶‚ÂÔü1zôè±cǪ¨¨HçM555¤ó^ͪ­­�7ožŽŽÎÑ£GõõõõôôöíÛ×£GooïŠŠŠÆ–>|øþýûþùç“'OJ3m³Xµo !?üðƒ½½}JJŠ––ÓY P�" �0aÂŒ3~ÿýw…½Ì“––6eÊÑ5DeeåéÓ§çää\¾|¹‰çÎ�ëëëëíí%� ²éðáëV­RØ£ @v¡0…æéé9wî܃*))îŸÀÍ›7 !ƒ ŸIOFFF6½ìæÍ›ÝÝݧL™òôéÓöK(ÓäfÈ€¢QÜoPL^^^3gÎÜ»w/‡ÃéÒ¥Kœÿ÷øñc//¯N�:Ñ“„�7oÞ|óÍ7=zôèСCÇŽG�}ëÖ-ñ54Ñ€´[ZZG¯³‰s ëÖ­377WWW×ÕÕõðð ­©©!„lذ�^\tÙ(""‚ž£§§WUéééîîî|>_]]] ˆ�­NOO'„ÔDlhhHyòäIÓ»QIIé÷ßïÑ£‡‡‡Ç»wïšnLo߀ £FDD„ššÚ—_~YSSÓX›ñãÇBoݺUZZš˜˜¨¬¬üúõë¼¼¼îÝ»wíÚ5,,¬¨¨èñãÇ“&Mâp8¤l¶EQC‡­ÿ¦�@WW7!!�žôööæóù×®]+++ËÏϧªìÖ­[M¬ÇÚÚºS§Nâs¬¬¬ø|¾@ ˆ��-..NNN¶´´ìСCTTÝ€þ%¸ÄÄDñ¥222!”`wR¹¹¹ÆÆÆŽŽŽ’´Wœ}+ÎÐÐPYYY’ýl€ÂÅÕ«WÕÔÔæÎ�ÛDUDýÿ—wxxx�ùsæÌ!„œ>}Z4§¼¼ÜÀÀ€ÇãåççKÒ€jüËÛÑѱcÇŽñññôd÷îÝíííŘšš¶âË›"*(Šzðà!ÄÊÊŠžl°0¢ÏY[[7°kòçŸjjj~ùå—’4Vœ}+…€lÁ¥4P×®]?~ü´iÓ:$ɸ¢!C†Ô™sñâEBˆ»»»hŽªªª³³ó‡®^½*Iƒ&DEE½}ûÖÎÎŽžtss‹��_°`Abb"}•çñãÇ#FŒ�`Cÿ‡ššš���h²_¿~÷ïßÏËË#„èèèBJKKÅ¡'é—$Ñ¿ÿ   ãÇ�oß¾]ÂEa߀ìBaòïÖ­[&L˜6mÚáÇ%m­¡¡!>YQQQTT¤¦¦VçÖkúÇ1òóó›mТÀ{öì9qâDVV–³³³¶¶6ý¤¥­�F�ãŸÓ¥KBÈ«W¯!æææ„�çÏŸ‹7xñâ!¤E?;f̘-[¶|÷Ýw!!!’´W„} ² …ȹ¤¤¤ñãÇ�3FÂsE RUUåóùåååÅÅÅâóég÷uëÖ­Ùôd�¯ÒÆp8œ/¾øâÆ�………—.]¢(jÒ¤Iâ§d”””*++Å),,¬¿ž¢¢¢:sè¯mú+\ BRRRÄГÎÎÎ’äùöÛo}||f̘‘œœÜ¢‰œî[�aL_ËhG÷ïß×ÕÕ7n\ee¥„‹Ðã`>|øPg~›ŒƒéÒ¥ËàÁƒé›šš8p Á |>ÿÑ£G¢É²²2‡ãää$šcjjj`` šÌËËSQQipLjjªhN�q0555}úô100mluuuïÞ½���ëo~³ªªª\]] rrrk£8ûVÆÈF ·žŸÿìÙ³øøx.—ûŸÿü‡ž_çÎ)>Ÿïèèxÿþýòòò—/_Ò¿¿±aÃÑz–,YBÙµkWqqñÓ§O½¼¼ ëykhh888$&&–””4xçTBB‚ššÚ´iÓòòò |||¸\nDD„ä»K\QQQß¾} PRR‚}+‚Â@¶ 0ùôÏ?ÿ|úé§¶¶¶ÅÅÅ.’��ÐôùÔ‚‚‚eË–uïÞ]EE…Ïç�5*22²E ÒÓÓ‡ ¦¡¡all¼gÏÑüaƉß9•ššêããÓ»woúY;¶¶¶¬­­µ/,,ôööÖ××çñxÉÉÉÖÖÖtæ•+Wnݺ•þ·¡¡á�;w�¦¦&�ÇsttŒ��­³Q÷îÝ=z´¶¶¶¦¦¦““Sý-’��Ý¥Kww÷êêjñù ¸oÃÂÂ꟡¾°‡RÔ_É9öâÅ‹áÇkiiݺu«cÇŽLÇQ,qqqÎÎÎ_ýõ¯¿þÊt€Ãàk�7#GŽär¹W¯^EU$}C‡=qâĶmÛöïßÏt€Ã‚\)**rss«¨¨ˆ‰‰¡oçéóòòJKKûúë¯{öìéââÂt€À¥4�eeennnYYY·oßîÞ½;ÓqEQ_|ñEXXX||¼……Óq$…ÂäDeeå¸qãîݻݻwo¦ã)//wrrÊÏÏOJJêܹ3Óq$‚1F ªªª&Ožœ˜˜�ªˆ%ÔÔÔBCC•••'MšTQQÁt‰ 0™WSS3sæÌèèè«W¯8�é8ð_zzz¡¡¡>œ3gNN€L@a2ï_ÿúWhhhhh¨ø�zKôîÝ;(((88xãÆ�Lgh #�m~~~§N�jŤƒtŒ9rß¾}?üðC`` ÓYš�ÛõA†8pàÇܵk×äÉ“™ÎMñöö~øðá¼yóºwïnggÇt€Fá®4�U¡¡¡“&Mòóó[»v-ÓY yµµµ'NŒ��OLLìÑ£Óq†ÂdRtt´››Ûœ9söíÛÇt�TII‰ƒƒCUUU||<ŸÏg:@P�ìù믿ƒƒƒ•••™Ž-�››kcccaaqùòe.—ò€u0ødLvvö¨Q£ú÷ïæÌTE2ÇÀÀ $$$66vÑ¢ELghÎ�,)((pppPUU�ŽŽÖÑÑa:´Ò… <==ýýý—,YÂt€ÿ¡ìççÇt‰»ºº–––ÞºuKOO�é8Ðz½{÷îСÃÊ•+ `ffÆt€ÿÂ#� UUU÷îÝ‹��555e:´ŸÓ§OÇÆÆZZZ2�@…ÈŠ¢fÏžrëÖ-üè‡Ü¨ªªrss{üøqRR’¡¡!ÓqAa2aùòåûöí wrrb: ´¥·oßÚÙÙiiiÅÄĨ««3w¥ëùûûïܹóøñ㨊ä�®®îüñìٳٳg×ÖÖ2…°[XXØ·ß~»yóæ©S§2�Ú…‰‰ÉùóçÃÂÂÖ­[Çt\JKNN3fÌ`: ´¯cÇŽ}ùå—ÇŽ›5kÓY@¡¡0–ÊÊʲ³³4hPHH‘¬V¯^ýÛo¿]½zU 0� #`£7oÞ :TSS3**JSS“é8 E͘1ãÚµk‰‰‰½zõb:((FÀ:ååå.../^¼HHHèÖ­Óq@z>|ø  :vìÈtPD| ìR[[;sæÌ‡†……¡*R4<ïÒ¥K>|˜0aBee%Óq@¡0vñõõ »páBß¾}™Î èÖ­[xxøýû÷.\ÈtPD(Œ€E8°cǎÇã‘EŠÌÂÂâôéÓ'NœØºu+ÓY@áàGd�-._¾%%EKKKjiA޵îˆÂq ˜p~ÚKLLLZZÚ¿þõ/�GÏQVVž>}ºŸŸßåË—'OžÜØ‚‡-ðñZwDá8PL8cíåæÍ›„�Aƒ‰Ï¤'###›XßFжZwDá8PL(ŒQEEźuëÌÍÍÕÕÕuuu=<>~Á‚‰‰‰ô•‹Ç��1¢¥ëQSS³±±Möë×ÏÀÀàþýûyyy„BHii©ø"ô$ý-**êíÛ·vvv­Ü�YÒ9%„ãh(ŒÑž={Nœ8‘••åì쬭­íæævñâÅV¬§S§Nâç~!]ºt!„¼zõŠbnnNyþü¹xƒ/^BZwbäŒtŽC€Aa¤ˆ8Î_|qãÆ�ÂÂÂK—.Q5iÒ¤íÛ·‹())UVVŠ/Bª£¨¨¨Îú«ˆþZ„�””ñô¤³³sÛl È2é‡-‚ÂHéèèзŒ©¨¨¸ºº^ºt‰Ãá\¹rEÔ@__Ÿ>µCËÏÏöìYýõ”””Ü¿_4ù×_åææZYYéëëBûôé\^^N7¨©©9s挱±±»»{;mÈé‡-‚ÂHA-\¸ðÁƒ¯^½úõ×_)Šrrr½:räÈÜÜÜÝ»w—””dff.]º´Áÿ|khh,Y²$))©´´ôîÝ»3gÎìСƒ¿¿?ýª’’ÒáÇ߾};wîÜüüü7oÞ,^¼8##ãàÁƒjjj¢•àn E&…ãPBíqnÚ´iÞ¼y¿üòËéÓ§“’’^¿~݆+€öB�|!„5Ý&55ÕÇǧwïÞôóclmm<(º+‡¢¨ÂÂBooo}}}�çàà�œœ,ºMzåÊ•[·n¥ÿmhhxçÎ�@ ©©Éãñcccë¼×½{÷F�­­­­©©éääT¿Á°aÃ:vì/šVÿ@=xð`îho¬:%9¢Úã8tqqqqq111ý­––Vÿþý'Mšäëë»wïÞˆˆˆ'OžTTTH¸N�…ÛSå ‡Ã òòòb:c°ؽ ¾ª««Ÿ={–%&333++‹2¥¬¬ldddjjÚ§OŸÞ½{›››[XX4øK�üV@ûâr¹&&&&&&uæ¿}ûVT*=~ü8!!áèÑ£ïß¿'„èééõéÓÇÜܼ_¿~––––––â¹€öƒÂ€ººººººu~Oðùóçééé�=úÏþ“žž~îÜ9úIñŸ}ö]$YYYYYYõìÙSI ƒDÚ #9QZZzëÖ­ˆˆ¦ƒÀG122222rqqÍÉÍÍýÏþ“–––’’±uëÖÊÊJMMM+++ëÿ×»woÔIm…‘lKKK‹ˆˆˆˆˆ¸}ûvee倘NmÌÀÀÀÀÀ@T*}øðááÇ))))))111{÷î­®®ÖÑÑ¡+$;;;<ª ÕPÉž÷ïß߸qƒ®‡rrrôôô\]]F�Õµk×:�9ÃãñùdÖ¬Y³fÍ"„äææÆÅÅݸqãàÁƒk×®ÕÕÕuvvvqq3fŒ‘‘ÓI˜„ˆ(ŠŠ��=zôhpppee¥‡‡Ç… F�%þ›bmÅÀÀÀÓÓÓÓÓ“’••uãÆ�°°°¥K—úøøôéÓÇÃÃÃÅÅeøðá:t`:)€´á¹ ËÍÍݲe‹™™ÙðáÓ’’¾ÿþûœœœsçÎ�?UH�‰‰É‚  ÂÂÂApp°««k—.]¦M›D?Œ@AàŒ3***BCCOœ8¡¥¥åééyæÌ™�2� —††ÆØ±cÇŽKÉÈÈ �9s¦²²²@ ˜8qâ¸qãºuëÆtL€ö…3F׬Œ IDATÒ–››ëççgdd4mÚ´òòòÀÀÀüüü *öèÕ«×Ò¥K###_¾|yàÀ�·|ùrCCáC‡nݺ533“é€í…‘”P9qâÄO>ùäСCK—.}þüùõë×===qäLQQÓ ÍèêêΞ=ûÂ… —.]êÛ·ïo¿ýÖ³gO ??¿¿ÿþ›é€m …Q»«¬¬LÿÒ-€@aÔŽÞ¿¿mÛ¶=z,X°ÀÚÚ:555&&ÆËËKEE…éhm¯¬¬lÕªUnnn666Lg�vÄår]]]�9’ŸŸ¬««»dÉ}}ý &œ={öÇLø((ŒÚEAAÁš5k>ýôÓŸ~úÉËË+33óøñãVVVLçh/qqq}ûö=|øp``àÙ³g™ŽÒ ¦¦6qâÄsçνyóæäÉ“5553gÎìÖ­›��OJJ ÓéZ‹‚6õêÕ«•+WjjjvîÜù—_~yûö­”àwÓ!çÏŸ—ònWd5557när¹cÇŽÍÏϧgâ8$ yæççoݺÕÜÜœ2pàÀ½{÷2  e8E1ýé!'^¿~½uëÖ}ûö©««ûúú.Z´HCCCú1nß¾�ŸŸ/ý÷YµjÕŒ3e¢¬¬Ó‰Ú—µµuHHHppðš5k˜Î"« °ÿþÜÜÜü188¸G�7nÜ`:@PµÆ£G�FŽ9yòäAƒ¥§§ïرCOO�éPÒPXX8räÈÝ»wŸ={vóæÍ¢AEòÍÉÉéèÑ£[¶lÙ±cÓYdŸÏ_ºtéÓ§O \]]­­­OžûÌÏϯ°°�é\ ¸P5ƒ¢¨'NXZZ¦¥¥]¿~ýÀ�ºººL‡�ªÔÔTKKË›7o*øp:•³gÏjjjŽ?¾¬¬Œé8ò£sçÎ~~~OŸ>]°`Á¿ÿýo???üþ0…QSþùç''§yóæy{{?|øÐÙÙ™éDÒ–žžîææ6pàÀ��<�†¢««š••5gÎÜÀß¶ôôô~ùå—çÏŸ¯\¹r×®]=zôزe Æ�”¡0jT```ÿþý_½z•˜˜¸eË�Çt"i{ò䉓“SÏž=/^¼ˆ?33³K—.…„„üüóÏLg‘CZZZ+W®ÌÎÎ^´hÑO?ýÔ»wïS§NÕÖÖ2�  £¼ÿÞÇÇgæÌ™^^^wîܱ¶¶f:þùç�@`bb�sEu >ÜßßßÏÏ/,,Œé,òI[[{Æ £F�š;w®¥¥ååË—™ …Q]ñññ–––¡¡¡áááÀ3A1½ÿÞÃÃCOO/<<UQƒ.\8wîÜY³feee1�En8p௿þêÓ§�‡‡‡««ëŸþÉt(�s(ŒþÇ®]»A¿~ý}úõ×_ûúúzzz2�Efxxxøúú.Z´Ã_¤CCCcóæÍwïÞ­­­8pàÒ¥K‹‹‹™òƒ£à§"Ï�;7kÖ¬1cÆœ>¾C‡LÇ‘%ÕÕÕÎÎί^½ºwïžk 5E�}:$$ÄÇLJé8Ì«®®þúë¯G�5sæL¦³È°™3gzzzz{{¿}û–é, gäÈ‘iii�`Ô¨Q>>>¥¥¥L'Y¥p…Qeeå´iÓ._¾:f̦ã°ÂöíÛŸ>}ºgϦƒÈ¼ýû÷s8œ¯¿þšé ŠˆÏçŸòÝ¥–MSS“S�ººº••ÕöíÛkjjÚm#Q˜�,.##ƒÃáHÿaÓk×®íß¿ÿ„ >r=-Ú-„�ððpSSS.—û‘ï+‰vÊÖD—©««ÿöÛoÇ�¿uëVkKLAv{«?Ž,,,îܹcggçìì¼{÷îÖoLk)H‰$''Ï™3§{÷î<OWW·oß¾“'OÞ·o_fff¥– JüöÛoÊÊÊaaam¸N+++CCæÛ$&&òx¼©S§æææ¾~ýzþüù\.÷êÕ«m£]³Ñ�ñ?~<=ùþýûèèhKKKBÈòåË[3!$((èã·‚RŒ�,²zõjúO2--­ò6,--�Ãá„„„´Õ %Ù-OŸ>õðð°´´ÔÖÖVVVn«·nV›gk¶ËÆŽÛ§OŸÊÊÊV&–˜"ìöÖ}Ñjkk7lØÀáp–/_^SSÓŠ­øHŠÐA555¾¾¾\.÷»ï¾{ôèQyyy~~¾øpÞªªªvˆß.ä¿0ºzõª²²ò¶mÛÚvµÍL555úúúeeeôœêêj333ccãòòò¶ ÓNÙê|Ñâãã !êêê�}ÜK³0’ƒ�,jlhh8`ÀBˆ¯¯o{…®ÇÇǧW¯^møU!ɇìôéÓ7mÚTUUehhȶ/ɳIÒeÿý·††Æ¦M›ZZ2аÛ[÷q$.00PUUuÒ¤I¢?I©Q„Z³f !$  ÎüêêêÑ£G£0b‘¬¬,�9sæ´ùš›=˜èSèÿú׿ÄgúùùB‚ƒƒÛAÞ½{§¡¡áïï߆ë”äCVôµÄÂ/ɳIØe¿üò‹ººúßÿݺÌR„ÝÞº�£:bbbtuuAqq±$íÛŠÜwУG�”””¬­­\œ®_e¨0’ç1Fôƒ���÷îÝ+ýw¿yó&!dРAâ3éÉÈÈHéç÷1Ù?~Léܹ³žž^»””Üìä#GŽÌ™3gРA–––/_¾ —B³gÏÖÖÖΞ=[ ï%ŽÍ?%&y6 »Ì×××ØØ˜ ¿"»½Ž–~ 6ìöíÛ�?lûñ™î €€€ÚÚÚÆ~yÚÎÎŽ¢(é œjò\­^½ú¯¿þ:wî\�N­¨¨X·n�¹¹¹ººº®®®‡‡Ghh(=|�¾ÍápèÆôœÿðÒÓÓÝÝÝù|¾ººº@ Z›žžN©3¨ÍÐÐ�òäÉ“Æ2³9[IIÉíÛ·.\¨®®¾ÿþÆš±CØ–ííÛ·aaat�2wî\BÈ‘#G ߆Nœ81aÂ>ŸOOJg·´Û²IÞe:tøõ×_ƒ‚‚bccebÓd%[‹>ŽÄõéÓçæÍ›ùùù#GŽ,,,l¶=›w{²Ñ7ýÑC¾äÓ§¬ÚKtt´’’Ò±cÇê¿äííÍçó¯]»VVV–ŸŸïëëK¹uë–¨�††ÆÐ¡Cű¶¶îÔ©“ø+++>Ÿ/bcc‹‹‹“““---;tèE7puu%„$&&Š/•‘‘A8p hŽ@ ÐÕÕMHH`a6úÜufffçÏŸ¯¿WE!AAA¬Ú6ïdŠ¢víÚ%è¿~ýZEE…Ëå¾|ù²ÁÝÛV²³³9Ε+WDs¤³[Ä5qÉ€‘.“0Õò.suuµ±±©­­­ÿ«6�Í»½uG�ÉÊÊ266¶±±yÿþ}Ó-YµXÛAúúú„�¤¤¤cËù,ŒJKK{öì9vìØ_íÞ½»½½½øSSÓVL„ÑJQÔƒ!VVVôdƒß‹ô©ñ ±ŽŽŽ;vŒ��ga¶:õ«ªª²²²Ö¯_Ïáp&MšÔôàkVm›w2EQ¤!$%%E¼=éìì,‹ÙD(Š"„”••5цÍžlUUU¿ÿþ{\\\�ÿ¯,[¶ŒrôèÑÆ6á#ݹs§²²’þ,‘Îniödku—,[¶lóæÍu‚±gÓêcs6I>Žš0hРãÇ�ïÚµk×®] 6`óN`O6.—Üà«+V¬PRR¢ïG‘ íy:Š.\àp8111M´áóù�=M–••q8'''ÑSSSÑd^^žŠŠJƒ×eSSSEsê\—­©©éÓ§���Á‡è9ÕÕÕ½{÷666Íay¶¦Ï]7öP;BHPP«6¤öd;þ¼¹¹yý„ýõ!„Ïç·Ó“è6nÜ(¾�4éìq’_2`O¶�é²ÂÂB]]Ýõë׳sÓêcU¶Ö}Iè—_~QVVnðð¬Ú lÎöóÏ?B>\g~zzº¶¶v� ÷,'W…QEEE¯^½>ÿüó¦›ñù|GGÇû÷ï———¿|ù’~æÞ† D –,YBÙµkWqqñÓ§O½¼¼ ëL‰‰‰%%% ŽäOHHPSS›6mZ^^^AA]SGDDˆ¯§Î]¬ÊV´cvv6=ÚÑÐа±!\¢Âˆ=ÂÚ�~ü¸¢¢âùóç‡Ò××wpp())il)’«Âˆ~Èì?ÿüÓt³ÔÔTŸÞ½{ÓÏ~°µµ=xð øÐ³ÂÂBooo}}}�çàà�œœL�,#„¬\¹rëÖ­ô¿ ïܹ#455y<ž££clll�÷ºwïÞèÑ£µµµ555�œœê76l˜ø]ìɦ¡¡Qçä"‡ÃÑÒÒ²²²Z±bEw&Ó…{6„�;9''G´cmllÄÉÎÎßí]»vmlW·NmmmçÎ�·oß^g¾ÔvKXX©çàÁƒâm˜ê²&²µI—•””téÒeÅŠØí-ÊÖê�#ÉUVV:99Õ©±Ø³(w�È�;w¾øâ ccc---[[[ñ»_e‡¢¨ú,‹Þ¼yÓ½{÷ï¾ûî‡~`:‹Bãp8AAA^^^L�†=yòÄÌÌ,11ÑÆÆ†é,Šhûöík×®ÍÈÈ Ÿö ìñîÝ;##£ëׯ+++3#?ƒ¯wîÜ©¢¢B�€Æ¤¦¦r¹Üþýû3DA-Z´HWWWôŸu`�Ž;'&&nذ�é,À$9)ŒÞ¿¿k×®eË–iii1�€Õž>}úÉ'Ÿ¨ªª2DA©©©ùúú?(((..nË–-LgÈ|aTSS³cÇŽùóç·ÉCTä[EEÅ‹/P1N[[{É’%;vì(..f: 4`РA7nüñÇéGw‚B‘ùÂ(<<üÙ³g‹-b:€ ÈÏϯ©©166f:�¥K—VTT9r„é аåË—2döìÙUUULg©’ùÂhÿþý...’üô)ЃZ0� :uê4{öl da'%%¥C‡¥§§ÿöÛoLg©’íÂèÙ³gW¯^õñña:€l  #¦ƒ!„,[¶ìŸþ¹|ù2ÓA afffëׯ÷óóKKKc: H�lFèҥ˸q㘠èßÖæóùLBéÕ«×èÑ£qß>›ùúúZZZΛ7'ö‡ FUUUÇŽ›?¾ŠŠ ÓYdCaa¡††žîÈK—.�ŠŠJMMe:4LYYùÈ‘#©©©þþþLg)‘áÂèÊ•+ùùùÞÞÞL�………¸ŽÆ*®®®–––øÒe³¾}û®^½zýúõ/^¼`: Hƒ F§OŸvttÄý5’+//WSSc:ü�%K–œ>}:??Ÿé ШU«Véëë¯X±‚é ²Z•––^¹reêÔ©L�%Eq8¦SÀÿ˜5k–ŽŽÎþýû™�RUUݺuk```TTÓY ÝÉjaRQQ1qâD¦ƒ|UUÕùóçïÝ»·¼¼œé,ШñãÇ�=zÉ’%ÕÕÕLg�ö%«…Ñ™3g\]]ñ´k�‹-***:}ú4ÓA );wî|úô)ÎíÉ=™,Œ ¯]»†ëh ôõõ½¼¼¶oßNQÓY Q={ö\¶lÙºuë^¿~ÍthG2Y………B&L˜ÀtƒF¬µlÙ²‡FGG3š²víZ55µŸþ™é ÐŽdµ0>|8RÐRªªª>|`:4ÀÚÚÚÖÖöÀ�L�¦hjj®[·nÿþý™™™Lg�ö"{…Quuõõë×ÝÝÝ™ {´µµñsî¬åíí}áÂ\¦a9ooïž={®_¿žé Ð^d¯0Š��-,,3f ÓAd�––VII ²°Ó´iÓÔÔÔ~ÿýw¦ƒ@S¸\î�?þxïÞ=¦³@»�½ÂèÊ•+fff½zõb:€ìÑÒÒª­­-++c:4@CCcÚ´i@åÊrS¦L±±±ùþûï™íB& #\GhmmmBÈû÷ï™ óööNOOOHH`:4…ÃáüòË/·nÝb: ´=+Œž?þèÑ#777¦ƒÈ$ú–…¢¢"¦ƒ@Ã<`À€C‡1šáäääêꊑFrIÆ £èèè:ØÛÛ3@&uíÚ•‚Ÿåb³yóæ�9s¦°°�é ÐŒüñöíÛø‘ù#{…Ñ�!C444˜ “:w¢’——ÇthÔÌ™39Ι3g˜Ͱ³³x¦‘ü‘±Â(**ÊÑё鲊ÃátíÚ…›ñùüÉ“'ãjšLøá‡nÞ¼yûöm¦ƒ@[’¥Â(///##…ÀÇÐ××GaÄróçÏOIIÁÝàì'†þË/¿0ÚGü¾ÐsçÎ�;wŽÁ4M{õêUBB‚»»;—Ëe: 122Ú¾};³ØÙ_ááá Ð××g:Èÿ@‰Ü¹s‡Çãõë×�é MAÅÇÇ÷èуÆ~Œ÷ƒ�õòåËøøx777�ÇH€Öa¼ËØì #//¯„„;;;5�¢(6üØSNNNbb"ãÏa±úK¶ ¿d ú‹ÙΪªªRQQaä­[‡ ]ÆfuO½ØÙÙ�={–‘(2äìÙ³S§Ne:!è/É ¿d úK¶°¤¿ÐY’cI—±–,�1hW(Œ„P¡0Ba „Â@…€ #!FB(Œ„P¡0Ba $íÂhÛ¶m‡Ãá5Û855ÕÝÝ]GGGKKËÅÅ%..NüUMMMN=êêêVVVÛ·o¯©©ibÍïÞ½Û¿¿“““®®.�ÇëÕ«×çŸ~ÿþý�Ý<¹Ó†ý%.##ƒÃáØÚÚJý%¡õ!$<<ÜÔÔ”Ëå6Ý ýÕNÚ°¿ðy(íñ÷•œœ::ÚÒÒ’²|ùò&V>oÞ<.—»cÇŽ¼¼¼ÒÒÒ˜˜˜>}ú(++_¼xQ’üAAAuv#d®¿DV¯^M~iii’@µˆ$ýõôéSKKKmmmee妣¿ÚU[õ—"J­³¨¶ë¯šš___.—ûÝwß=zô¨¼¼}Ó¦MUUU†††MFè¯öÖVý¥ÈŸ‡l+Œ$é¯5kÖBê̯®®=zt³…‘¬w›±tŒQLLLZZÚ”)Sx<=GYYyúôé999—/_nzY3WìIDAT33BHYYYQQQcm:tàÀñ9VVV</33“¢¨�ޝpZÔ_×®]ãr¹„�“'OVWW7»~ôW›;|øðªU«š½ˆFÐ_ì yÕ�ÏCF4Û_ééé›7o¶¶¶ž?~�—”••øá‡¦×�.kW,-ŒnÞ¼I4h�øLz222²ée?~Léܹ³žžžäïXZZúáǾ}ûr8œÇUx-ê¯#GŽÌ™3gРA–––/_¾ oÅ;¢¿>’¨„mú‹ $ï¯:ðyȈfû+  ¶¶ÖÓÓ³ÁWíìì(ŠjQŒ.kC-.Œ***Ö­[gnn®®®®««ëááJ�ìÛ°a=0ÍÁÁ�nAÏiðo2==ÝÝÝ�Ïç««« ñ±ºééé„�:Ü !Ožyòd�eÑ_Òﯡ¿šÆ¶þ"ø}‚ƒƒËËËé555gΜ166vwwo6äíÛ· !ƒn¢�ŸŸ_ZZZHHˆªªj³+”]ì鯰°0==={{û:kž7o!äôéÓ>|hbCÐ_´6é/I ¿$Ážþj>EØÓ_¦¦¦ëׯ¿{÷nýÛ<?~|àÀ///úL|c¤ËÑšç-\¸ðÁƒ¯^½úõ×_)Šrrr½:räÈÜÜÜÝ»w—””dff.]º´Á"ZCCcÉ’%III¥¥¥wïÞ�9sf‡üýý…±””>üöíÛ¹sçæçç¿yófñâÅTSSk,XuuõßÿíççhhhøÍ7߈^rrrêÔ©“¨”>vìØ�?þ˜””¤¥¥%þÓBMÿ<�ŒbI=zôË/¿¬¿æ¾}û2¤¨¨èÂ… ¢™è¯ví/I ¿$Ä’þªŸ‡ bO­]»vÕªU .\µjÕ“'O*++_¼xqøða�@`iiyøðañÆŠÜe ?I.É�©©©>>>½{÷¦Ÿakk{ðàAÑ€yŠ¢ ½½½õõõy<žƒƒCrr²µµ5ý^+W®Üºu+ýoCCÃ;wîMMM�çèè[ç½îÝ»7zôhmmmMMM''§: 444êl ‡ÃÑÒÒ²²²Z±bE�;Ї Ö±cÇøøxz²‰ÓN�Ý)Ž%÷:ÊJåääˆv¯���ø"ÙÙÙâ;¿k×®ô|ôW{÷WXXXý�yðàA ýõÿd¥¿h þy(áíúìé/‘;wî|ñÅÆÆÆ***ZZZ¶¶¶þþþušÉ_—±‡»ýÏËË‹röìÙÆö8ÐΞ=;uêTŠé'¯£¿$„þ’-è/Ù†þBgµºŒÍXú“ Ò‡Â@…€ #!FB(Œ„P¡0Ba „Â@…€ #!FB(Œ„P¡0Ba Ä­3���àååÅH’““Ãt!ô—$Ð_²ý%[XÒ_è,ɱ¤ËXë #OOO¦rÈccc;;;¦S ¿$…þ’-è/Ù†þbmg¥§§jii1ä°¡ËØŒCQÓä‡Ã ©,Ù‚1FB(Œ„P¡0Ba „Â@…€ #!FB(Œ„P¡0Ba „Â@…€ #!FB(Œ„P¡0Ba „Â@…€ #!FìµmÛ6‡ÃápŒŒŒ$injjÊår›h“œœû7¤7¤†.&šP[[ëééÙà«vvv,9WÂì†àR@;ª¨¨X·n�¹¹¹ººº®®®‡‡GhhhMM !dÆ ôxdºqDD=GOO¯þªÒÓÓÝÝÝù|¾ººº@ ˆ‹‹kQ’˜˜Bˆ¥¥¥BmHuuuPP�««k·nÝx<^¿~ýüýýkkk]€€ÿkïþCªºÿ8ŽŽ¿ò¦ezËŸslcÈ�R‰ÛHSÑkØÜ– “ þ¡)­F¥3˜á†Fr›­š3†ÈÀáPÖZ2lºJwý1H#R‡mš3'÷:Ó]SÏ÷�s¸ˆúõ{¯­¯Óž�¿üœó>ïû>þã‹óãú !ª««ÓÓÓ½¼¼~øá‡ñññÁÁÁ£G� !meQQQ³ÔëõZ­vö–ððp///ƒÁpóæÍ¿þú«½½=,,ÌÍÍ­©©iþç9;;Ïß „hmmµs~ƒÁàããóóÏ?+Ëz"—/_B�ŒŒ’$ÙY933£de¹rO$...//ÏÛÛ{ãÆ�‡Þ·oŸÑh]°˜`À3”˜˜ØÒÒràÀ“ɤÜxêé鉋‹s´�»»ûŽ;lËÐÐÐÀÀÀÎηÚÙ!00P1<Z­vÎ5___!ÄÐÐ��bcc…]]]Køt±bOÄb±„††z{{+�=;vL1>>¾`=Á€gH’¤ÔÔÔ††³Ù\WW'Ërrrò™3glNNN“““³1›ÍóûX,–9[”$¡¤ {dffº¸¸ÔÔÔ,¸÷øñãNNNÝÝÝ«ìD’’’ 322z{{•›ƒ¥¥¥Bù¿¼¹F0àÚ°aƒòGÚÕÕ5!!¡®®N’¤+W®Ø úûûmËÁÁÁÌï366ÖÙÙi[Þ¾}{`` <<\yÙ!!!'Ožìèè¸téÒœ]===/^LIIÑét«éD¦§§›››ýýý�9²iÓ&åJÕÄÄÄ"Í F<[YYY]]]V«uhh¨¸¸X–åøøxÛÞÝ»w ”••���õõõegg/xíÄÃÃãСC­­­�?îèèØ¿¿›››Ñhth’üüüÜÜܬ¬¬ÜÜÜÞÞÞÉÉÉþþþŠŠ ƒÁVQQ1»8>>^«ÕšL¦•{"ÎÎÎqqqƒƒƒ%%%ÃÃÃ���.\X¬µ=¯ºG !ª««oݺ•™™¹yófåë"""ÊËËm/|ɲl6›ÓÓÓ4Mttt{{»^¯WþFçää”””(?µµµ OOO�F{óæÍÙ§¼—>GyyùüÁÚÚÚRSSƒƒƒ]]]×­[a4­V뜲˜˜ooï––e¹BOäÑ£G™™™J�ŸŸ_ZZZnn®ÒS¯×ÏïÉ¿à™øÿ%þo¸• "¨F*‚€Š` "¨F*‚€Š` "¨F*‚€Š` "¨F*‚€Š` "¨F*‚€Š` "¨F*‚€Š` "¨F*—åÇܸqcppp¹§°‹Éd’$i¹§øœ��ß|óMww÷åä_A’ey¹gÀ®®®SSSË=ŪòÍ7ß$''/÷ÿ \1¬0SSSÕÕÕ)))Ë=È*!IAÓ†gŒT#Á@E0PŒT#Á@E0PŒT#Á@E0PŒT#æ:}ú´$I’$½ð öÔÿý÷!!!...‹Ô´··§¥¥½üòË�ÆÇÇgëÖ­ï¼óÎùóçûúúžfTY–›››ßÿý���5kÖøúúFGGWUUɲü4mŸ[#æ:zô¨,Ëáááÿ³²¯¯oïÞ½yyyüñÇ«™™™9vìØÎ�;}}}¯^½j6›ïÞ½[ZZ:::zðàÁW_}ujjjÉ£öôôDGG÷ööÖÔÔX,“Éôâ‹/¦¦¦;vlÉ=Ÿg#–îĉ;wîüå—_Ö­[·HÍéÓ§?ûì³ââb�N·fÍ??¿„„„úúú7Þxãégpqqùúë¯ÃÂÂÜÝÝ_yå•/¿üR«Õ–••Y­Ö§oþ¼Yì¢X\EE…F£Y¤ »»û“O>Ñëõsv9;;Ÿ8qâêÕ«O3€N§{òäÉì-nnnÁÁÁ·nÝúûï¿×¬Yó4ÍŸC#–nñT$„øüóÏgffÞ}÷Ý÷FFFþã™Íæ_ýuÛ¶m^^^ÿlçç·Ò«�Õj-((Ðétk×®õññIJJúöÛo§§§…§N�R¬ŽŽŽVŠëëë•-7nœßª»»û­·ÞòòòZ»v­Á`hnnvh’ëׯ !ÂÂÂì)žššª®®NHHð÷÷×h4¡¡¡F£qffÆÎÏmnnÞ»w¯¿¿ee¥CsBA0¬B‡:{öì§Ÿ~úçŸÞ½{W§Ó½ýöÛ7nÜBäçç˲ìááa+NLL”eY¯×Ïï366vðàÁ?ü°¿¿ÿúõë###ñññ?ýô“ý“ !´Z­=Åõõõï½÷^||üÝ»wûí·|ðÁ999³kâããµZ­Édšsì©S§¼¼¼¢££���kkk·nÝjÿ�°!V¡k×®mÙ²%!!A£Ñøùù•””„„„,¡�Åb)**ŠŠŠòôôܾ}{UUÕäädvv¶£}$I²³2...//ÏÛÛ{ãÆ�‡Þ·oŸÑhµÌÌÌȲ<ÿ\~~¾ÕjURà¶mÛ ‚`X•[ZZ8`2™”;h===qqqŽöqwwß±c‡mØÙÙùðáC;; !†‡‡í)Þ³gOccãì-áááOž<¹sçŽmKSSÓÈÈHddäüÃÝÜÜt:Ýùóç÷îÝ[PPÐÐÐ`ç�°!V¡sçÎUVVÞ»wo×®]ëׯOLL¬­­]B­V;çb�¯¯¯bhhÈα±±Bˆ®®.{Š-KAAAhh¨···òØ“òuDããã��””$„øî»ï: ‚`X•$IJMMmhh0›Íuuu²,'''Ÿ9sÆVàää4999û³Ù<¿�Åb™³E‰DJ<²Gff¦‹‹KMMÍ‚{�?îääÔÝÝ­,“’’ 322z{{•[f¥¥¥BGß\SÞÒqè(‚`UÚ°aƒ’6\]]êêê$Iºr劭  ¿¿ß¶|ðàÁü>ccc���¶åíÛ·ÂÃÃìœ$$$ääÉ“—.]𳫧§çâÅ‹))):�N1==ÝÜÜìïïäÈ‘M›6)Wª&&&ïôèÑýû÷ÏÙ¨|7Òk¯½fç�°!V§¬¬¬®®.«Õ:44T\\,Ër||¼mïîÝ»ÊÊÊÆÆÆúúú²³³¼äááqèСÖÖÖÇ�wttìß¿ßÍÍÍh4:4I~~~nnnVVVnnnooïäädEE…Á` «¨¨PÊœ��ãââKJJ†‡‡'&&/\¸0§Ûü·Ò¾úê«�>úèþýûV«õþýû999UUUz½>==Ý¡9!„2+Š¢ººzñš[·neffnÞ¼Yù£ˆˆˆòòrÛû\²,›Íæôôô€€�FÝÞÞn{]?''§¤¤Dù9((¨­­Í`0xzzj4𨨨›7oÎþ Ë—/ÏÿÛZ^^>¤¶¶¶ÔÔÔàà`WW×uëÖEDD�F«Õ:»æÑ£G™™™J�ŸŸ_ZZZnn®ÒS¯×+5111ÞÞÞ---ÊÒb±|ñů¿þúK/½äæææéé©×ë?þøãñññð÷ùü�dþû.`E‘$©ºº:%%e¹Y%ø}ÎÆ­4Á@E0PŒT#Á@E0PŒT#Á@E0PŒT#Á@E0PŒT#•Ër€ÃL&“$IË=V!I–ååžÿþûïË=Åêáââòã�?ÆÄÄ,÷ ÿ #ϨF*‚€Š` úlD}tózPìIEND®B`‚anytree-2.13.0/docs/static/tree.png000066400000000000000000000740501500063244000171300ustar00rootroot00000000000000‰PNG  IHDR.[òM0bKGDÿÿÿ ½§“ IDATxœìÝy\�éÿ?ðשs’6mJ*!D¥‘¢TÊ6¶ìÓH ʾÌoƒi0Ƙ1˜˜0c™1C iì2Ú£hUÖdO¥¤Eû©ë÷Ç|œï2–Ó¹N�÷óñð‡sîîûuºïÎyŸûÚŒ1B!„ù¤Ä;!„BÈ›¢Â…B!�.„Bi4„¼BÞ_uu5?~Œ¼¼<�1†ââbÔÕÕACC"‘êêêÐÖÖ†‰‰ 455yG&„�wB… !�HII �žžŽ+W® ==÷ïßG^^Þ[íG]]¦¦¦°´´„¥¥%ºvíŠ=z }ûö ”œB¤C@£Š‘_555ˆŠŠÂ©S§�””ÔÖÖ¢U«V°¶¶†µµ5Ú·o´jÕ †††ÐÑÑ´hÑJJJ(--…X,Fyy9 ñàÁäååáîÝ»¸ví222�™™ ±X ¸¸¸ÀÕÕÆ CË–-9ÿ!äAT¸"gêêêpæÌãøñã(,,„µµ5\]]áìì '''Jõ˜UUU¸t颢¢�˜˜TWWÃÉÉ £G�ÆÇ,)ˆ!„#*\‘øù矱cÇܽ{½{÷ƨQ£àéé)ó&œ²²2œ:u GŽÁ±cÇ ‹1~üxÌœ92ÍB!ÿ@… !¼`Ë–-ظq#Æ��9sæ k×®¼£JKK±ÿ~lÛ¶ )))èÓ§V¯^ WWWÞÑ!Ї& #„—ÊÊJ¬Zµ m۶ŶmÛðÅ_àÑ£Gؾ}»Ü- ©© ___$''㯿þ‚’’ÜÜÜ0dÈdffòŽGQ0T¸ÂÁÙ³gaccƒo¿ý+V¬ÀíÛ·ñé§ŸBMM�w´×êß¿?¢££qîÜ9dgg£k×®X¹r%*++yG#„(*\‘¡ªª*,\¸D×®]qõêUøûûË}ÁòonnnHJJÂ×_�M›6¡wïÞ¸~ý:ïX„@… !2rïÞ=8::bçÎ�Ø»w/> SSSÞ±Þ™P(ÄÂ… ‘šš ôèÑûöíã‹ÒÄQáBˆ ¤¥¥ÁÑѵµµHNNÆÇÌ;’Ô´oß±±±ðõõÅĉñÍ7ßðŽDiÂhæ\BØùóç1dÈôèÑ!!!ÐÒÒâIêD"¾ÿþ{´oßóçÏG~~>¾ûî;Þ±!M.„4 ŒŒ :...8xð TTTxGjPsæÌ�¾¾>&Nœ]]],[¶Œw$BHC… ! $77ƒ ‚�� ‚ƒƒ›|ÑòÜ„ PTT„Y³fÁÄÄ“&Mâ‰Ò„Ðt„4ÆŒ¬¬,$&&¢E‹¼#ÉÜâÅ‹ñã�?")) ¼ãBšš9—�†°yóf,Z´qqq°··ç‡‹ššôíÛb±  é/!ä½Ñ̹„H[~~>¾øâ ,]ºTa‹àï»{÷îEFFvîÜÉ;!¤‰ ;.„HÙܹsqøðadffB]]�wî,X€ 33¼ãB7ºãBˆ4açÎ�X¾|9-ÿ³|ùr”””à÷ßç…ÒPáBˆíß¿àååÅ9É»yðà�Ô÷©¯¯�Q£Fa×®]Rß7!DñPáBˆ8p£G�†¶¶6ï(oíÎ�; Vp}òÉ'¸xñ"²²²dÿ„ÅA… !RR]]�‹/ÂÃÃw”·öðáC :ùùù ²ÿ¾}û¢yóæˆ��m�ýB�O$DJRRRPQQ�Þ½{Ku¿µµµˆ‰‰Ahh(BCC///ܹsiiiPRRš5k ¬¬ŒêêjdddÀÚÚ+V¬€ŽŽ ¸¸øµÛìÙ³W¯^E‹-0cÆ lÛ¶Mª¯A$ÁÎÎçÏŸ‡���T÷MQ,4ªˆ)9xð ¼¼¼P]] %%éÝ̬ªªBRRÜÝÝQQQ�µk×ÂÞÞÁÁÁX½z5úõë///?~,™?%99ÊÊʰ³³{í6ÚÚÚ°°°Àõë×¥–ýŸ¦N�Šììlœ>}ºAöOQ4ªˆi)((€ŽŽŽT‹hÖ¬ajj ðó󃻻;~þùglÙ²™™™ðóó“lo``€åË—ãÎ�;X»v-Ö­[÷ŸÛÈ‚¾¾>žww÷7Úæ9±XÜ`Y?~L… !ä½QáBˆ”X[[RSSdÿ•••ðBsËâÅ‹aee…ÀÀ@äääHߺu+1gΜ7ÚÌÍÍ‘““ƒû÷ï7HþäädtíÚµAöMQÔÇ…)177‡¡¡!Ο?/Õ!ÑeeeذaîÝ»øôÓO1sæLØÚÚBMM .\ÀêÕ«áãヮ]»BYYúúú‡H$‚H$úÏm`ìØ±Ø³g.]º„6mÚH-?>>ˆ��m°¹P«�;wÂÓÓ“ŠBÈ{£Â…)òðð€™™~øáÞQäFBBΟ?�iÓ¦ñŽBi¨©ˆ)Ûµküüü�––KKKÞq¸sssƒX,Ftt4ï(„�Æ/ˆ B¤¬¶¶¶¶¶hݺ5N�:%™8NÃËË çÏŸG¯^½xÇ!„4~ÔÇ…iSVVÆÎ�;®ÐMF<ÀìÙ³1kÖ,*Z!RCw\i _}õV¯^�ððp8::òŽ#SåååpssCyy9мysÞ‘!M5ÒPêêê0zôhDGG#::VVV¼#ÉDMM <==‘��€ØØXXXXðŽDi:¨©ˆ�†¢¤¤„ýû÷ÃÚÚ @FFïH ®ªª ü1¢¢¢pâÄ *Z!RG… ! HUUG�EÇŽáä䄨¨(Þ‘Lqq1Œ°°0=z¼#Bš *\i`-Z´À™3gàáá��bóæÍhj-´iiièÕ«nÞ¼‰èèh¸ººòŽDi¢¨p!Dš5k†àà`,_¾Ÿ}öFŒ�Ç�óŽõÞcزe zõêCCC$$$ÀÆÆ†w,BHF… !2¢¤¤„åË—#""iiièܹ3¶mÛ†ºº:ÞÑÞIJJ úô郅 ÂßßçÎ�ƒ±±1ïX„�&Ž Bd¬oß¾¸rå ¦M›†yóæÁÁÁ'Ožäë�ݽ{¾¾¾°··‡²²2eeeÞÑ! € B8ÐÐÐÀúõ둚š ccc :={öDhh(jkkyÇ{¥k×®aúôéèÔ©Î�;‡Ý»w#::|ðïh„B… !YZZâÏ?ÿDbb"ŒŒŒ0zôh˜™™á‹/¾ÀÝ»wyÇCEEöïßXZZ"""Û¶mÃõë×áíí­ÐËBø  è‘#1bŠŠŠPXXˆ=z`Ô¨Q>|¸Ì&°{òä ‚S§N¡ªª ˜7o-Z%%ú¾Cá†fÎ%D^ÔÔÔ`ذaHNNFxx8>|ˆ��üùçŸxüø1 àää'''ØØØÀÆÆzzzïu̪ª*\»v ˆ��GTT®\¹¡PWWWŒ=#FŒ€¿¿?BBB;;;)½bBykT¸"êêêàåå…S§N!""Ý»w—²²² ‹¡¢¢[[[899ÁÙÙNNNÐÖÖ–d¨©©Áˆ#péÒ%šÆŸÂ.„ȃ  ((Ç�‡‡‡ÇnÿèÑ#\¹rééé¸wï²³³‘““ƒÇ�£¤¤555(--…X,†––”••¡­­ ---˜˜˜ U«V011�¥¥%¬­­Ñ±cGˆD¢×³¼¼xðàâââ`jj*­—O!oŠ Bx Àš5kŒ1cÆðŽóZOž<�““”••]]]Þ‘!Š…Y$„§mÛ¶aÕªU ’û¢ôôôpöìY”––bÈ�!(++㉢`¨p!„“ÐÐPÌ™3kÖ¬ÁôéÓyÇycÆÆÆ8{ö,îܹƒñãÇC,óŽDQ T¸ÂAxx8&L˜???,[¶Œwœ·Ö±cG;v QQQ˜…§§'*++yG"„4T¸"EÇŽÃ'Ÿ|‚•+WbÁ‚¼ãpgjjŠ“'O"--�Ö5"„H.„HITTÆ�‡©S§" €w¹amm�“'Oâܹs˜6mc¼#B1*\‘‚ôôtxzzbРAغu+ï8r§W¯^Æï¿ÿŽ•+WòŽCiÄh:BÞÓíÛ·1pà@tëÖ ÁÁÁPVVæI. :»wïÆ¤I“ ««‹… òŽDi„¨p!ä=äççcðàÁ000@HHH“Y¨¡Lœ8�=Âÿûÿºººðññá‰ÒÈPáBÈ;*))Á AƒPWW‡3gÎ@[[›w¤FañâÅÈÏÏÇôéÓa``€ÁƒóŽDiD¨p!äTWWc̘1ÈÍÍE\\ yGjTÖ¯_�ÂÂBŒ3aaaèÓ§ïH„�F‚ BÞRmm-¼¼¼pñâEDFF¢mÛ¶¼#5:�;vì@QQFŒ�èèhXZZòŽEihT!o�1†3fàäÉ“8vìºuëÆ;R£¥¬¬Œß~û –––0`îÝ»Ç;!¤ Â…�·°lÙ2ìÞ½¿ÿþ;œœœxÇiôš7oŽcÇŽ¡eË–ðððÀãÇ�yG"„È9*\yC[·nÅ7ß|ƒ;vÀÓÓ“wœ&£E‹8qâÄb1†ŠgÏžñŽD‘cT¸òöíÛ‡yóæaýúõøä“OxÇirZ·n�³gÏâÁƒ1bªªªxG"„È)*\ùýõ¦L™‚Å‹ã³Ï>ã§É277Ç™3g�œœŒÉ“'£®®Žw$Bˆ¢Â…�×HHHÀÈ‘#1~üx¬]»–wœ&ÏÆÆGŽAhh(æÌ™Ã;!DQáBH=®\¹‚!C†ÀÍÍ »ví‚@ àI!¸¸¸ 88?ýôV¯^Í;!DÎÐ<.„¼ÂÇ1dÈXXX 88B!ý©ÈÒˆ#°uëVøùù¡E‹˜7oïH„9AïÆ„üKAA<<<$£]ÔÔÔxGRH¾¾¾ÈËËÃÂ… add„±cÇòŽD‘T¸òååå>|8ª««Þ‘ÚŠ+ðôéSLœ8-Z´À€xG"„pF… !ÿSSSƒQ£FáÖ­[ˆ‰‰�‘‘ïHÀ† $ëEDDÀÎÎŽw$BGÔ9—uuuðööÆ… púôiXXXðŽDþG à§Ÿ~Bß¾}1hРܸqƒw$BGT¸àÓO?Å‘#GpèÐ!tïÞ�wò/"‘‡B§N�àáá�ðŽDá„ ¢ð°eËüþûïðððà‡ÔCMM G�…††† ‚ÂÂBÞ‘!PáBš¼ÚÚZ>|555/=·mÛ6¬Zµ AAA3f ‡tämèééáìÙ³(--Ň~ˆ²²²—¶‰��GVV‡t„Y Â…4yüñÆŒƒ¡C‡¾ðA÷|vÖ5kÖ`úôé’·allŒ³gÏâöíÛ˜0aÄb±ä¹={ö oß¾´ž!M˜€1Æx‡ ¤!988 )) JJJèÖ­Μ9ƒ´´4 <S§NÅÖ­[yG$ïàâÅ‹èß¿?Fމ_ýß}÷üýýÁƒ@ @zz:¬¬¬xÇ$„HW.¤IKNN~aø¬H$B«V­ðôéS >{÷î…’Ýxl¬NŸ>�áÇÃÎÎñññ’ÇE"¦L™‚íÛ·sLGiT¸�¦mÒ¤I~¡‹P(„ªª*âããéy#W[[ DFFâßoeÍš5ãG� ««Ë)!¤ÑWMÒdåçç¿T´€X,Fee%qáÂNéÈûªªªÂèÑ£ýRÑü]ÔìÞ½›C2BHC¢Â…4Y;vìxåðwñRVV†þýûãìÙ³2NFÞ×Ó§Oáââ‚'N ¶¶ö•ÛˆÅblÞ¼uuu2NGiHÔTDš¤šš˜˜˜àñãǯÝN @]]ÅÅÅÔ×¥Y°`6oÞüFÛþùçŸ>|x'"„È5‘¦)$$ùùùõ>ÿ¼H±··GXX-�ÌÊ•+±`ÁˆD"ˆD¢z·SVVƦM›d˜ŒÒÐèÝš4I6lxe1" ¤¤„víÚáàÁƒˆ��GïÞ½9$$ïCWW7nDVV||| ¤¤¡ðå5ckkk�ŒŒ ) ! �šŠˆÌ={ö >DQQž={±XŒÒÒRhkkttt ¯¯�V­Z¡Y³foµÿ”””W®7$ ¡§§‡U«Vá“O>yåiœ®^½ŠåË—ãÈ‘# …/LJ§¢¢‚©S§âÇ|ëýæåå!//EEE¨®®Fyy9ªªª ¢¢uuuˆD"hkkÃÐІ††Ò|Y„�—ÑphÒpnß¾�ÄÄDdddàÊ•+¸víîß¿ÿÊiÚ_§eË–hÓ¦ ¬¬¬`ee…®]»¢gÏžõsõññÁþýû%£‰D"TUUñùçŸcÞ¼yhÞ¼ù{¿6"Ÿ.\¸€O?ýñññPVV–tÜmÖ¬rrr ££óÒψÅb$''#-- W®\AFF233‘››‹êêê7>¶H$‚¡¡!:uêKKKX[[ãƒ>€��Ýk›³!o… "=ùùù8vì"""‰‡B(¢C‡°¶¶F—.]жm[´jÕ &&&ÐÕÕ…ºº:„B!455ÁCQQ ¨¨Hòm÷Áƒ¸wïÒÓÓqåÊ<|øJJJ°¶¶F¿~ý0`À¸»»CUUùùù066FMM D".\ÿW~h‘¦‡1†��,^¼wïÞ•Œ,ûþûï±`ÁÀåË—qüøqDEEáüùóxöì455Ñ¥KtíÚhݺ5LLL`hh]]]ˆD"4oÞªªª¨ªªByy9Äb1 ‘——‡‡"''7nÜ�ë%%%PSSCï޽ѯ_?|øá‡´ú8!ï‡ ò~žiÄb1víÚ…+VàñãÇ022‚——BCC‘••###¸¹¹ÁÙÙÎÎΰ°°�zSÏÍ›7ƒ¨¨(DFFâÁƒhÛ¶-<==áíí [[[©�@… y7.\@PPþøã¨¨¨`ذaðôôÄ Aƒ ®®.Ó,�=Ÿþ‰��œ;w***ðññÁ矎6mÚÈ4 ‘/ÕÕÕØ»w/V­Z…û÷ï£mÛ¶7n<==ѳgO™öIaŒ!99!!!8|ø0nܸøùùá£�>¢&LBÞL!o!&&† :”`–––lÓ¦M¬´´”w,‰ììl¶nÝ:Ö¦M&‰˜··7ËÌÌä‹ÈXUUûå—_˜¹¹9SVVfîîîìèÑ£¬®®Žw4‰ÄÄDæëëËš7oΠغuëXYYïX„È»©p!oäêÕ«¬ÿþ suue‘‘‘¼#½Vuu5ûùçŸY»ví˜H$bsçÎeEEE¼cf&&&LUU•Íž=›=xð€w¤×ÊÉÉa .djjjÌÈȈíÙ³G® ,Bä .äõÊÊʘ¿¿?‰DÌÎÎŽEEEñŽôVª««ÙŽ;XË–-Y«V­ØÞ½{yG" äÆ�¬ÿþL °©S§²‡òŽôVrssÙ¬Y³˜²²2ëÛ·/KOOç‰yô#M@GêuùòeØÛÛcûöíØ¸q#àììÌ;Ö[‰D˜>}:nܸ�‘#GÂÇÇãÆ�“Œ^"MÃÎ�;ѽ{wIÇíŸþÆÆÆ¼c½CCClݺ/^DMM ìíí±eË–z×Û"DQQáB^é§Ÿ~BÏž=¡§§‡Ë—/cöìÙPVVæë�éèè ((aaaˆ��…­­-’““yÇ"塚¼&L€¯¯/æÌ™ƒ‹/6ú™�»w¸8,]º ,€§§'JKKyÇ"DnPáB^ÀÃòåËáçç‡E‹!""¦¦¦¼cIMÿþý‘––†Ž;¢_¿~8sæ ïHä=yòîîîøë¯¿pæÌ¬[·®ÉLô¦¬¬Œ•+W""" pqqAnn.ïX„ÈM$cðóóÞ={°cÇLž<™w¤SSSƒiÓ¦aÿþýØ»w/Æ�Ï;y ¹¹¹puuEUUN�: Þ‘Ì�;w0hÐ ÔÔÔ 22’†øEGó¸�ÿ³xñblÚ´ !!!:t(ï8 Ž1†… "((G�ÅÀ�yG"o ¸¸ýúõCee%"""`ddÄ;Rƒ+((@ÿþýQ]]�˜˜èëëóŽD/T¸�¿ýðÃX¸p!öîÝ ///Þqd†1†É“'#$$111èÖ­ïHä5Äb1<<<�™™‰¸¸8˜™™ñŽ$3999èÓ§ õÖ‹�ÒDQ‚¤¤$,Z´kÖ¬Q¨¢vî܉=z`„ o½$‘­Õ«W#!!'OžT¨¢ŒŒŒpúôi\½zK—.å‡n莋‚«¨¨@·nÝ`jjа°0())f-›���nݺÁÓÓ;vìà‡¼B\\úõë‡ÀÀ@Ìœ9“wn~ûí7Lš4 §N�¢æM¢ˆ¨©HÑ}ýõ×X»v-®_¿Þèæ½�¶ÀËË /^„��ï8äêêêààà=== `ìØ±ÈÈÈ@zz:„B!ï8„È5)²üü|¬[·‹-’›¢¥¸¸˜Û±Ç�‡ž={bÑ¢EÜ2�WÛ¿?RSS±aÃÞQð½Nà›o¾ÁíÛ·ñÓO?qÍAtÇE�­]»7nÄÝ»we¾¢ó?‰Åb|÷Ýw8~ü8âãã!‹ëÝ–1†]»váôéÓèÔ©òòòàææ&µ¾9QQQpqqAbb"Ýu‘#¶¶¶°±±Á/¿üÂ-ÃÛ\§ÀßÍ�gΜÁéÓ§ñàÁ\¸pAªyæÌ™ƒÓ§O#33S¦«\­­¨êêêX‡ØÂ… yGaŒ1V^^ÎtttðúKòË/¿dfff¬°°�1ÆXaa!333c›6m’Z–.]º°Y³fImäý$&&2ìüùó¼£¼ñuúܽ{÷faa!õ,ééé ‹ˆˆ�ú¾ ‘c´V‘¢ºténݺ%7“Ì5oÞ¯ÝæÞ½{X½z5üüü ££àï©ü§OŸŽeË–áÉ“'RÉ2yòd8puuuRÙy?ÁÁÁ°°°�‹©üßä:ý§†œ,ÎÚÚ=zôÀþýûì„È#*\TLL `ccÃ;Êûý÷ß!‹Ñ¿ÿwssCyy9~þùg©ÇÝÝOž<Áµkפ²?ò~bbbàîîÎ;†\rwwGLL ï„È. êÂ… ptt”ê>/]º„ž={böìÙX±b„B!JKK±}ûvI;|II 6lØðÂcÿ”™OÞ-¢ IDAT™‰aÆAGGöööˆˆˆÄÆÆLLL^ØþùZJiiiRy666ÐÐÐÀùó祲?òî*++‘’’"Õkµ¡¯SYêÓ§®_¿Ž§OŸÊüØ„pû±ŠðaggÇüýý¥ºÏŽ;2VWWÇclܸq,//�1ÆXûöí_êðïÇ,,,6þ|ƶmÛÆÔÔÔ˜’’KKKc|ðÀÊËË_ØOYYÀzõê%µ×bccÃ>ÿüs©í�¼›Û·o3,11QjûlèëôßÐ@}\cìÆ� KMMm�ý"‡~¤ Ô“'O §§'Õ}>}úOŸ>Å?ü€¹sçbÅŠPUU€W®Ú[ßJ¾«V­‚––€¿¿q/X°6l�<öïo¿Ïÿ_]]-µ×¢¯¯/µ>3äÝ€T¯Õ†¾Ne9òéùïåùï‰E@ME ª¼¼Í›7—ê>ƒ‚‚ ¡¡� ÀÁÁÏž=“¼±¿�þÌÈ‘#W¯^EçÎ�EEE/lÿü6yëÖ­ß5úKÔÕÕñìÙ3©í�¼›ŠŠ �êµÚÐש,=ŸÆ€–ª Š„ ¥££#õvñ1cÆ 55 @RRœœœ°gÏž÷Ú§¡¡!€¿GgXYY=zôÂ6999€¾}û¾×±þ©°°Pêw¤ÈÛ{>zLš×jC_§²TXXÐÕÕ•éq በ¥¯¯�üü|©îsåÊ•077Ç™3g°oß>ˆÅb,_¾Àÿ5çTUUø{ ÷ç³�²×Ì�øàÁÀСCáíí�-Z¼Ô 2<<"‘Hª DæççSá"žŸi^« }�ÊÒóß‹¾¾¾L�KOT¸((+++¤¤¤HuŸß}÷�ä›ñ˜1c ¥¥%YJày3Ïš5k�™™‰Í›7K>Μ9ƒÚÚZɇÆóo‘Œ1lܸÇÇäÉ“¡««‹¥K—bûöí(--ð÷È�;v`ùòå’ÑE﫤¤YYYèÚµ«TöGÞ�‘‘ôõõ‘œœ,µ}6ôuúOååå€ÚÚZ©åÿ§¤¤$4oÞíÛ·o�ý"�hÊõ믿ÂÏÏEEEhÖ¬™Tö)`kk‹qãÆ!==ÅÅÅ D»vípóæMx{{###}úôÁ–-[0uêT´mÛ~ø!<==�~ø¥¥¥èر#š7o ̘1ÊÊÊþoÊÿÈÈH´iÓ7oÞÄ€0mÚ4©M{†�"77WÒ@ø6lÔÔÔpàÀ©ìO×)DDD`ÿþýøé§Ÿ  ñÕW_aÀ€èÖ­›T^LŸ>7oÞDTT”ÔöIˆœ£Õ¡Õýû÷Ñ®];„„„`Ĉ¼ãÈ•™3g"66ééé¼£›6mB@@rrr¤Þ¡¼1‹Å055ÅÌ™3±råJÞq‘ZZQµiÓ®®®Øµkï(r¥¢¢ÁÁÁ˜2e ï(ä¼¼¼P^^ŽÃ‡óŽ"WŽ?޼¼¼ã"KÔÇEÑ�:u C† ÁéÓ§1pà@Þq¸Z¸p!~ùåܺu‹æÅ�CS§NÅ_ý…´´4hkkóŽÃMYYºwïüñǼã"kÔÇEÑ <ü1&Mš„ÜÜ\Þq¸9sæ 6oތ͛7SÑ"§Ö­[±XŒéÓ§óŽÂÕüùó‘ŸŸ� 6ðŽBT¸lݺ˜8q¢T×ûi,nß¾�I“&áã�?†··7ï8¤-[¶Ä/¿ü‚��òŽÃÅîÝ»±k×.ìÞ½[æ³ô"/¨p!hÑ¢:„K—.ÁÇÇuuu¼#ÉL^^SSSñŽCþƒ»»;¾úê+,X°äG¦Ž;___,[¶Œ¦0 � °µµEhh(Ž9‚éÓ§C,óŽÔà=zœ}úHšZjkkÙ‰'ØðáÙ²²2SWWgcÆŒaûöíc2Ë–™™É6nÜÈœœœ˜’’ÓÖÖfsæÌaééé2Ë@ä[QQ dÖÖÖ kÓ¦ ›?>‹ŒŒdUUU2ÉPSSÃbccÙgŸ}ÆÚ·oϰÎ�;³ï¿ÿž=yòä�öyíÚ56vìX€¹»»³ÔÔT)§&D.ýH‹,’z1ư~ýz,[¶ S§NÅ–-[ ¢¢òÒv¹¹¹ EHH"##!‹aee…~ýú¡W¯^°²²‚¥¥%š5kö^yž>}ŠŒŒ \¾|qqqˆŽŽFvv6´µµ1lØ0Œ5 DóæÍßë8¤éJNNFHHŽ9‚«W¯BEE}úô�³³3ììì`mm�¶mÛ¾÷ž»wïâÊ•+HJJBLL âââPQQ�N�:aÔ¨Q5jìíí¥òš.\¸€Ï>û ñññ=z4¾ýö[âOš2Zš¼Zee%|}}±ÿ~¬Y³þþþoôsEEEˆ‰‰Att4bbb�’’‚êêj…B´oßfff022BëÖ­¡££mmmhkkC,£´´b±%%%ÈËËCNN²³³qçÎdggø{‰GGG899ÁÙÙ‰D ùë MЦM›ðé§ŸbøðáÈÈÈ@VV@CC�:u‚±±1Z·n ###´hÑjjjhÖ¬ÔÔÔPQQ�ÊÊJTTT ¸¸¹¹¹ÈÎÎÆ£G�póæMɨ¦víÚ¡oß¾ˆ‰‰�‰‰ bbbäµ0ÆpèÐ!,Y²¹¹¹˜;w.–-[--­9!QáB^öèÑ#Œ9YYY8xð ú÷ïÿÎû‹Å¸yó&®\¹‚«W¯âáÇ’b¤¸¸EEE¨««Cqq1”••¡¥¥¡PMMM¢U«V011�©©)ºví KKKZ\޼·ÒÒRtéÒ~ø!¶oß())ÁÕ«W‘‘‘�›7oJ®ÓÜÜ\”––¢¬¬ UUU(//GóæÍ¡ªª 555hjjJ®ÓV­Z¡cÇŽ°¶¶†••Z´h¸xñ"z÷î�àà`Œ;¶Á^Wuu5‚‚‚¡PˆåË—cöìÙ … vLBdŒ ò¢øøxŒ5 ššš8zô(,,,dr\;;; 0_ýµLŽGÛgŸ}†Ý»wãÆ�Ð××—É1§M›†'Nàúõë’‚¦¡býúõؼy3ÌḬ̀zõê-˜‘¡ š€ŽHÃÍÍ |ð.^¼(³¢…Yºzõ*~øá¬]»VfE ¬_¿µµµhðcéêêbݺu¸qã0~üx8::"..®Á�MHC£Â…€1†€€|ôÑG˜>}:Ž?Þàß áeΜ9°±±Á´iÓdzÜçÅD`` RRRdrÌ6mÚà×_EBBTTTàää„qãÆáöíÛ29>! � WZZ OOO¬[·{öìÁæÍ›i!7ÒdíÛ·QQQغu+—ë|Ê”)prr‚ŸŸêêêdv\{{{DFF",, ×®]C—.]àçç‡üü|™e DZ¨pQ`YYYèÕ«.^¼ˆ¨¨(øøøðŽDHƒ)--ÅâÅ‹1mÚ4ôìÙ“K�@€-[¶ 55?ýô“Ì�ïî”Jú°}óÍ7¨¬¬”yBÞ. *::½{÷†ŠŠ âãã¹½‘"+_~ù%***ðÕW_qÍaee… Àßß9992?¾P(„¯¯/nݺ¬Y³رc‡Lïò®¨pQ@;v쀻»;\]]GËI“Ç«Cn} ««ûÆó#5uuuøûûãúõë4hfÍš…ž={"**Š[&BÞ. D,cîܹ˜1c–-[†àà`¨©©ñŽEHƒãÕ!·>jjjظq#öîÝ‹ððp®YŒ��±}ûv\¾|­Zµ‚‹‹ <<<�žžÎ5!õ¡ÂEA|8fΜ‰ªª*Þq`ii‰cÇŽáìÙ³(((@÷îÝáçç‡ÜÜ\ÞÑy. ==ööö¸u뢢¢àééÉ;!2!r_'00ÙÙÙØ°aï(îîîHJJ¾}û†:`É’%(--å�T¸4y'OžDß¾}allŒÄÄDtïÞ�w$BdF^:äÖ§M›6X¾|9Ö¬Y#Ws«())aìØ±¸rå V¬X�mÛ¶¡sçÎØ±cjkkyÇ# Ž —&Š1†o¾ùÆ Ä pîÜ9ðŽEˆÌÈ[‡Üú|öÙgèØ±#fÏžÍ;ÊKÔÔÔàïï�¬¬,x{{cîܹèÚµ+þøãÞш£Â¥ ª¬¬„��–/_޵k×bûöíPQQá‹™’·¹õ …غu+Μ9ƒ#GŽðŽóJzzzX·nÒÓÓamm�ñãÇKæ„!DÖ¨pib=zgggœ8q§OŸæ:Ü’^äµCn}úöí‹I“&aþüùxöìï8õêÔ©<ˆóçÏ£ªª =zôÀ¸qãp÷î]Þш¡Â¥ ‰��G�=P\\ŒóçÏ£ÿþ¼#"sòÞ!·>6l@EE…La|_½zõBLL BCC‘œœ ÌŸ?ÅÅż£@…K±ÿ~ZٙȇÜúèééaíڵؼy3RSSyÇy#Æ õk׈àà`˜››ã›o¾‘‹áݤé¢Â¥‘{¾²³——­ìL^cé�[Ÿ©S§ÂÁÁAæ‹0¾‘H___\¿~Ó¦MC@@€¤/cŒw<ÒQáÒˆ•––bäÈ‘´²3!ÿ3wîÜFÑ!·>JJJضmRRR°k×.ÞqÞŠŽŽÖ­[‡›7oÂÕÕ&L@ïÞ½Ã;ib¨pi¤ž¯ì|éÒ%ZÙ™üÝ!722[¶liÔ|×®]1wî\øûû#??Ÿwœ·fjjŠíÛ·ãâÅ‹PSSƒ³³3† †[·nñŽFš*\!ZÙ™�ý³Cn¯^½xÇyo’9T+;;;„‡‡ãìÙ³¸ÿ>,--áçç‡Ç�óŽF9*\™ç+;»¹¹ÑÊ΄üOcí�[MMMlÚ´ {öìAdd$ï8ïåù|/?ÿü3Ž;sss ¢¢‚w4ÒHQáÒHü{eçýû÷ÓÊ΄ ñwÈ­ÏèÑ£ñá‡bΜ9¨©©áç½())aÒ¤IÈÌÌÄòå˱qãFtêÔ‰– ï„ —FàŸ+;‡„„ÐÊ΄üCcï�û:[¶lÁÝ»w±qãFÞQ¤B]]]²„À¨Q£0{öl888 <<œw4ÒˆPá"çþ¹²stt4FŽÉ;!r£©tÈ­�™™–.]Š/¿üwîÜáGjôõõ±yóf¤§§ÃÜÜýû÷‡‡‡._¾Ì;i¨p‘c'Nœxaeg[[[Þ‘‘M­Cn}-Z333,\¸�w©ëܹ3<ˆsçΡ°°¶¶¶˜4irrrxG#rŒ 9ô|eçáÇÓÊ΄ԣ©uÈ­�ŠŠ ¶mÛ†£G�âèÑ£¼ã4777$&&"88±±±èС–,Y‚’’Þш¢ÂEÎTVVbÒ¤I´²3!¯ÑT;äÖÇÙÙü1æÎ�+׋0¾�@€±cÇâúõë’÷>ssslÞ¼b±˜w<"G¨p‘#ÙÙÙpvvÆÉ“'iegB^£)wÈ­Ïwß}‡ÒÒR¬Y³†w”¥¢¢‚ùóç#++ S§N…¿¿¿d B*\ä­ìLÈ›iêrëchhˆ¯¾ú 6lPˆN¬ººº’%ìíí1~üx8::âüùó¼£Ψp‘ÏWv¶µµ¥•� y Eé�[???ôèѳgÏV˜ Û´iƒ_ýñññ‰DèÛ·/Æ�‡Û·oóŽF8¡Â…£ÚÚZ,Y²„Vv&ä )J‡Üú())aûöíˆ��Ç/¿üÂ;ŽL988 ** aaa¸zõ*ºté‚ùó磨¨ˆw4"cT¸pRZZŠQ£FI¦õÞ¼y3””ètREë�[Ìš5 Ÿ}ö xÇ‘¹çKâÀ�077Ç7ß|ƒÊÊJÞшŒÐ'%´²3!oO;äÖgÍš5PUUŲeËxGáB$Á××·nÝÂܹsñå—_¿þú«Â4¡)2£³,SÑÑÑ3f Œ��ñçŸ*ä"‰aaaX¼xñ k”Ü»wêêê/|“VWWDZcÇвeK1 'W¯^…¥¥å �íÛ·ÞÞÞˆ‹‹SȾ-¯ràÀxyy!&&ŽŽŽ/øàtêÔé…ÇD"¼½½©ýYÅÇÇ×{AYY®®®ˆ��W¨ùIêóÁ 55½zõ‚P(|å6555ˆ��•q²Æ£W¯^ˆ‰‰App0’““aee…%K–¼²éŸÈ*\ÞRUUfΜùÊÖ»wïbË–-puuå�¬qúwsQMM >úè#މ/—.]ª·ïʬY³pêÔ)ZËëôõõŽ™3g¾òùçMEÏû‘— Œ;×®]ÃÆ�±k×.ÉÕÕÕõþ\NN¼½½‘ŸŸ/ôD‚Û€¦FêË/¿|aØá?ÿ)++3]]]vÿþ}Þ1�[·n1�@ ùššš²ºº:Þ±ˆŒÝ¹sç•OB¡�íÞ½›w<¹·}ûv& _ùÞtíÚ5Þñ�ÂÂBæïïÏTUUY§N�ØÁƒ_ù~äããðnݺ±gÏžqHªÐhº·qïÞ=¬]»ö…©êÿ©¶¶%%%2dÊÊÊdœ®q277‡­­-D"¦L™�@À;‘±K—.½pÞE"455qîÜ9Lž<™_°FÂ××ÐÖÖ~ᦲ²2õsy :::X·nnÞ¼ L˜0ŽŽŽ/4¹]¾|{÷îddd`Ô¨Q¯í$M¤� —·0cÆŒÿ¼í*‹‘‘‘� È(Uã7iÒ$7M˜0�sÂÃ¥K—$¸B¡:t@JJ œ��9'k<úöí‹ÔÔTXZZJú½())QáòLMM±}ûv$$$@UUNNN6l²²²ðé§ŸJº ˆÅbœ;w>>> ?D_–^Ý«KÕÔÔ   xöì™äŽFII jkk¡¤¤$iÿVSSƒ††ôõõ¡¯¯ÿÚ‘ o*44§OŸ~í6"‘555hß¾=\\\Þû˜MÑ?ÏaQQH¦ÿ755Ž{÷��� ¡PMMMèêê¢eË–ÐÐÐà›üKuuõ ç²¼¼Àÿ=êéé½r~žøøxTWWCII C† Áï¿ÿNçü˜˜˜ ..>>>’ÑÑÑõn_XXˆüü|<{ö OŸ>TVVJ†›kjjB(B$ACCzzzhÙ²%ÔÕÕeòzxëÑ£"""pôèQøûû£sçÎ/Í™S[[‹ mÛ¶X»v­Ló)êù“«™scX»v-V¬X�@€ÀÀ@<|øPr=z„‚‚‚WN\÷&š7o}}}˜™™¡C‡’¿ÇÎ�;ÃÊʪINQ]]�N�:ááÇõ¾�mÚ´ óçÏ—Ú1KKK‘ššŠ›7o¾ðw¨àç/ˆ[áRVV†ØØXÄÆÆ")) III’Y3 $¿LsssÂÐÐúúúhÙ²%´´´Ð¼ys€––”••QWW'y3­¬¬Dqq±äa^^òòòpûömɉÏÍÍðwÏ|;;;tïÞ}úô�³³óK û-[¶ ß~û­ä"PRR‚@ €‡‡¦L™‚#FHåÎNcRSSƒ„„DGG#99III’¹Z´h!9íÛ·‡±±1ôõõahh(9‡ZZZþ.JžŸÏçߨŸŸÏ'Ož ??_r.ïÞ½‹¬¬,ܾ}wîÜAUU„B!,--ѽ{wØÛÛÃÅÅ–––¼~-�Raa!"##qáÂ$''#99YrGÌØØu’ª IDATíÛ·‡¹¹9Úµk‡V­ZÁÀÀ@rGóMþKJJ$ç0??999’syëÖ-dggK~ÞÖÖÝ»wG¯^½àêꊖ-[òù¥42uuuHKKCdd$‘””„›7o‚155µ> Œ��ѲeKÀÀÀZZZ’;d¯ú{|Þïùù+((ÀãÇ�%çðyQ[SSUUUØØØÀÎÎpuu}£/ˆònÏž=øä“O^Û$$pðàAŒ3æ­÷ÿªó—™™‰ºº:¨««¿ð…MÁÏŸl —¤¤$;v çÎ�CBBjjjйsgØÙÙIþuëÖMò�Ö�JKK‘––&)š’’’píÚ5(++ÃÞÞnnn6l´´´`cc±X,i êÞ½;>ùä|ôÑGÐÕÕmð¬òäÆ� EDDbccQVVSSSØÛÛ£{÷î’††† ž¥®®YYY’¢)99—.]BII ZµjWWW¸»»cĈÐÓÓkð<�Imm-"##qòäIDDD -- �ÖÖÖ’¿ÅîÝ»ÃÆÆF&“½UTT ==]r.“’’pùòeÔÕÕÁÚÚnnnøà€éèè0vìØ1V]]Í;"999ì‡~`NNNLII‰ihh°aƱ�7²´´4¹šN ¢¢‚…‡‡³åÿ¿½;��é^ÿþ™ÌdßdO$‘�ˆH"!»XŠJ(mB-÷^K•ro©J×VÛ­R[é-E‹JKQAÄVA ¤–H‚¬’ˆìë<¿?ÜÌO²Ì™“‰çýzÍëÅÌ™ï÷™y2gž9ç{¾ßùó) €¤R)éëëÓ¨Q£h÷îÝTRR"vˆu²dÉ’®dÿì­:'ñññµ¶Åùk´u‚.ùùù´qãFòõõ%äàà@|ð�>}ºI%åU®]»Fï½÷988rww§¥K—Rvv¶Ø¡ ®´´”vïÞM}ûö%‰DBFû÷ï§ŠŠ ±Ã«³ââbÚ¿?………‘¡¡!iiiÑÈ‘#éèÑ£jõ·Ø©©©´téRrtt$äááAŸ}ö™Úá)))ôÕW_Q×®]I"‘�µµ5Í™3‡ÅMpr¹œŽ=J#GŽ$MMMÒÓÓ£ÁƒÓÖ­[©  @ìðê,;;›¶nÝJƒ&MMM266¦¿ÿýï'vh/µtéR²¶¶®1Ç�––Ö ‹™LFVVVŠ9½8J¥ÜÂ%==�fÏžM†††¤««KãÆ�£?þøC™]ˆæÂ… 4qâDÒ××'===zÿý÷éþýûb‡¥tÙÙÙ4þ|211©ñ%_UU%vh�VXXH[¶l¡Î�;+ Ñï¿ÿ^­ ±ú¸|ù2�1‚¤R)YYYÑœ9sèöíÛb‡¥IIIôÉ'ŸPË–-ICCƒ‚ƒƒéüùób‡¥tååå´eËrss#Ô­[7úá‡ÔæHÅˤ§§ÓâÅ‹ÉÉɉ$ õíÛ—Nœ8!vX/•——GgÏž¥o¿ý–fΜI½{÷& ‹‹ŽŽŽâÿnnn´fÍΟr)§pÉÈÈ ™3g’®®.ÙØØÐŠ+(77WM79yyy´zõj²··'mmmš2eJ³(`²³³iΜ9dhhHæææôïÿ›222ÄK0W®\¡ñãÇ“¦¦&999ѦM›šMsñâE &‰DB¾¾¾´k×.*++;,ATTTPDD½ñÆ€ú÷ïß,~,UTTЦM›ÈÑÑ‘´´´hÒ¤ItíÚ5±ÃDUU:tˆzõêE¨gÏž-vXõ’››K§OŸ¦ 6Ðûï¿OÝ»wW 3�J¥œ?åj\áR^^N«V­"###²µµ¥¯¿þºYT’uQVVF6l GGGÒ××§Å‹Sii©ØaÕ[EE­Y³†LMMÉÊÊŠ–-[¦V‡.+))‰þñ��––yzzÒñãÇÅ©Á222hüøñ¤¡¡A�;w¦¼6§ÃˆˆŽ9B=zô ‰DBcÆŒ¡ˆRƒDGGSûöíIKK‹&OžLÉÉÉb‡¤2§N�¢¾}û ¡””±Cª·§ó7~üø&7PH*Ê_à —³gÏ’‡‡éèèPxx8)30µQZZJ‹-"===rssS«_ çÏŸ'///ÒÒÒ¢9sæ¼V˳nß¾Mƒ&4jÔ(ÊÊÊ;¤:“Ëå´fÍ266&{{{Úµk—Ø!‰jß¾}äììL´bÅ µ9Í™��McÆŒ!ÔlNë5Äï¿ÿN­[·&===Zºt)UVVŠÒ+qþþŸÀù«áRUUE .$™LF x-ÆÕEJJ :”¤R)}òÉ'Mú´CUU-^¼˜455©oß¾tëÖ-±Cj2}út255ULÅDýû÷'???êß¿?íÞ½[°×L~~~‚´ý,¹\N¾¾¾4dÈ•ô÷*ÊÌam*++é�7Þ|mnn.YXXмyóíçiýõihh4™´>¹$"zðàmÙ²…FŽIo¼ñÆ+·WU.#""H"‘¨ì »5kÖ�žžžbá=±)3�bíW·mÛFR©T%sQ5çüU“Ëå´k×. "êׯÓÔ©SiÉ’%4kÖ,¥¾†æ¯öÂ%&&†Ð©S§”�“‰‰ /OüÅ_�ƒƒƒb­¤ÜÜ\rpp ¯¾úJ±MZZšb¬j¥¥¥4cÆ @Ë—/ä5\¾|™$ >|X�öŸvðàA@—.]¼¯ºRfŸõË/¿úöÛo•sm-ZDFFF*;ÍðÞ{ïQ›6mšÔ °uÍeµ””@mÚ´y嶪̥ŸŸ…„„ÞOyy9988ÐôéÓï«>”™G1ö«•••Ôºukš4i’ÒÛ~Úë�¿¬¬,êÕ«¹¸¸Ð¹sç…fUUýðÃdjjJ&LPjü Ì_í…ËèÑ£) @9‘)Q›6m^š ääd’Éd´xñâ÷/\¸�ôôôƒSåry­É+//'rrrR~ðÿÓ¿}j܈ââblÞ¼ ‘Hj}¾¦¦& ‘ŸŸ/XŒ#GŽDTTJJJ룤¤ÑÑÑÍ:‡O»zõ*\]]1{öl$$$àðáÂÆhllŒ7ß|¿ÿþ» ýÀ‘#G@D>|¸à}5ªÎepp0tttïçСCèܹ3íGLbíWGŒ�ªª*;vL�ö�柿ˆˆÄÄÄ`îܹ033«u›^½zaäÈ‘Jï»!ù{®pùóÏ?‘››‹(5¸ê¶;wîŒiÓ¦!<<2™ ظq#$‰â???+W®¬qßÓnß¾�àà`˜˜˜ S§NˆŽŽœ9s`ggWc{{{{OvŠ/³gÏ<|ø&Lhôk}‘ ´´T«N�:…ÒÒR¼ùæ›Jo»)æpݺu˜9s&&Nœ¬\¹R©¯¹6ĉ'PZZ*h?GŽA@@Z´h¡ô¶…ÎeC¨:—:::èÕ«"##í'22R�}*Ð4óø4¡÷«&&&èܹ³ 9lîù‹ˆˆ€ç~0>kĈõxeuÓ ü={ fëÖ­¤£££ÔCAÕÜÜÜÈÄÄDqîlÔ¨QŠùaœ��Ÿ;Üõì}Õ‡Äf̘AGŽ¡ 6�žžihhÐÕ«WÉÛÛ›Pqqq�vŠŠŠ@�IÈÈȈÆ�Gï¾û.P‹-hãÆ�‚ž¶°°tæÇÕ«W“µµµ m7¥=9/;qâDÅÿçÍ›GèòåËJ}ÝϺ|ù2|P`·nÝ›;Fè\> ¯8U$V.çÍ›GÞÞÞ‚µ_\\L›÷£)åQ¬ýêÔ©S©W¯^‚´ý:ä¯cÇŽ@´«“ꙿçǸ,[¶Œ”T5sss@_}õUUUQ||¼b&×ÚÎÓ={_õÿŸžýõ«¯¾"4vìXêÞ½;xîj„ê?<___Å}ÈÅÅ…’““éÆ�I“'O&mmmš5k– se´oßžÂÃÃkÞ¼yäãã#HÛM)‡DOƾ<ýÅ–žžNÚÚÚª´×\›ŒŒ @'Ož´WWWZ´h‘ m �Ëg½ªp+—kÖ¬!KKKÁÚOJJ"táÂAÚoJyk¿úÅ_�»»» m¿ùëܹ3 ´´4e¾´:«gþžãRXXƒº²©‡õë×ÃÀÀ3g΄¿¿? addTïvž~ΰaÃ7nÜ€»»; //¯Æö�=´lÙ²Æý2™ hÛ¶-ú÷ï�õë×cùòåXµj–/_^ï¸êJèq4¯EËËËñÍ7ß C‡ŠÃ§666(++ÃO?ý„Ô;®º244Aó<ùÄÎeAA�`í€Ú~&ëKŒýª‘‘‘`9|òçááHHH¨w¿ÊPßü=W¸XXX ;;[©AU Á•+Wп\ºt Ý»wÇ÷ßߨ6­¬¬­ZµB»víiii5¶IOOtëÖí•íUhÝ·o_£âz™¬¬,XZZ Ö¾……>|(HÛM)‡{öìÁ?ÿùOQ�ÛöíÛQYY‰5kÖ4*®—ÉÌ̬»PÔ9—õ!v.…Ì£……¼y|¡÷«BîS_‡üõìÙpîܹFõÛPõÍßs…‹••²³³Q^^®ÔÀàÓO?…‹‹ "##±sçNTVVbþüùþDzYY@.—ãñãÇ"za›÷ïß <aaa066~nPÒñãÇ¡©©‰wÞyç•1fddlllêùêêF.—###ÖÖÖ‚´<‰=--í¥ï[C5•VUUaùòå;vìsí…„„ÀÂÂ7nìWXjj*šÇêö…:Ú t.ëJì\¦¥¥ šGsssH¥ÒçŠqei*y|¡÷«©©©‚Ÿ¯CþBCCáëë‹Õ«W¿ðu–––6º z‘zçïÙ“GÕçó¢££yÖêyºººŠIÅÊËËÉÈȈüýý‰ˆhذa€æÏŸO·nÝ¢U«V)&Öùý÷ß©²²’ÜÝÝ €bºu¹\NS¦L¡!C†(6-]º”ÜÜÜ(??Ÿˆˆ?~L®®®5VÓ­/aoo_c6ÇŒŒ  MMM:þ¼Ò_?ÑŸþI(..N�ö‰þà¨3‚6•nÛ¶�_çøñã }þùçJˆžLBgmm-øÌÒü19;; Ò¶*rY­zpµ««ësqˆ�KÁ'ëØ±£`SÓ7•<й_uvvtÜàë�¿7nP«V­ÈÉɉöîÝKŠçDEEQ`` ÅÄÄòÔ3µO@çîîNÿú׿”Õÿ :Ð’%Kè�wÞ¡   º{÷.ݼy“üýýIOO�úõëG7oÞ¤nݺQhh(ýøã�TZZJGŽ¡ÁƒSÏž=iÒ¤I4}útZ»vm�_ÕÓ҆†Ò¼yó($$„6mÚ¤HàÏ?ÿL#FŒ €üýýéÍ7ߤ€€rww§Ñ£GS||¼Ò_{µ �­­­ _xr¹œlllháÂ…Jo»)äpïÞ½diiI¦¦¦´nݺçbŒˆˆ ___@:::´téR¥¿Ý»w§ñãÇ+½Ýg�:uJ°«—T‘K"¢ãÇ�Óßþö7@2™Œ¾üòKÅ \±s™žž®’…3çÏŸ/ØlM!�bîW€ +E7÷üUËÏϧ¥K—Ò AƒÈÑёڵkGÞÞÞ4oÞ¼NL×X È_í…ËܹsÉÆÆFðµB^7äììLï¿ÿ¾à}M�:•\\\x%a\¿~�444hÿþý‚÷UQQAÖÖÖ‚ü�`O~H˜˜˜*éëu‘““C´råJÁûâü)_ó÷âÕ¡§N�J¶¶¶*[H®¹+,,$ggçZ¯«JXX9;;SAA�ÊúlîΟ?OR©”vîÜ©²> ÈÜܼÉ-ð¦îæÎ�KFFFбB‹ŽŽ&‚Ÿ–z�L›6�,,,¨°°Pð¾8Ê×Àü½¸pyôèÙÙÙQXXXã£c4eÊ233£ôôt•õ™••E–––4yòd•õÙœ•––R»ví¨oß¾‚Ê}ÖŽ;T¶²øë &&†¤R)mÚ´I¥ý†††’���ÊŠ¥æ,**Š$‰JDpþ”§ù{qáBDtàÀ’H$´uëÖ†GÇhïÞ½*ÿ€UÛµkI$Úµk—ÊûnNär9M˜0�Œ��éÞ½{¢ÄBvvv‚¯dÜÜeff’‹‹ 0@åhnn.ÙÚÚÒСCyüY#¤¦¦’½½= >\¥ýrþ”£‘ù{yáBôärLMMMŠŒŒlH¯½Ó§O“ŽŽM›6M´f̘AÚÚÚtüøqÑbPwááá$“É[¯¤.rss©]»väééɧp¨  €:vìH®®®”‘‘!J üñéêê vyms÷øñcòöö&wwwQÆ}qþG ù{uá"—ËiìØ±dhhÈ_|õC&&&4|øpQ«óªª*zûí·ÉØØ˜N�:%ZêjÉ’%$‘Hè»ï¾;ºwïÙÙÙQ@@®®§¼¼<êÝ»7YYYÑ�;wD�å—_~!©TJsæÌQùQuöèÑ#êÞ½;µlÙ’’““E‹ƒó×0JÊß« ¢'“âŒ=š´µµéÇlhg¯•}ûö‘žž 2¤I\V^VVF!!!¤­­Í§�ꨲ²’¦N�JR©TÐÕ¼ë+!!�ÈÝÝ]1ß{¹�——µlÙ²ÖsŰmÛ6ÒÔÔ¤qãÆQyy¹Øá4y÷ïß§öíÛ“��]»vMìp8õ¤ÄüÕ­p!zò«ýÃ?$ W̪Çjªªª¢%K–�T*¥¿ÿýïMê|˜ ¨wïÞ”šš*v8MÖÉ“'ÉÎÎŽÚµk'Ú³ÚpþêFÉù«{áRmÆ ¤««K]ºt¡¤¤¤ÆЬýôSÁ׬ÚáÇÉÝÝ�d2…††Ò�7ÄI%nß¾M&L MMMrvvVëCöÅÅÅôù矓®®.ÙØØÐªU«¨¨¨Hì°—’’BÓ¦M#jÕªíÙ³Gì�„ó'xþ_¸T;{ö, 8�PÇŽé»ï¾kvÉ*))¡~ø�õéÓ‡Nœ8!vXJséÒ%zë­·H"‘�§§'­[·NmN—ÔÇ�;whîܹdnnNôÏþS¥3 ­²²’¶oßN¤¡¡AÆ £Ã‡«lm%U‘ËåtìØ19r$I¥Rrss£ï¾û®ÙœºNOO§Ù³g“¾¾>YZZÒüùóE½X(gÏž¥±cÇ’––988кuëšÅx-Ο`”W¸T»pá‚âÒé-ZÐ|@.\Pë_ð±±±4{öl233#MMM t u±ÅÇÇÓøñãIOO�ôõõiâĉtâĉ&u…T}Ð�?þHýúõ# ²µµ¥Ï?ÿ¼Y/\XUUE{ö졞={’D"!'''Z¸p¡ès˜4VRR-]º”\]]Gzwîܩ֟/“••EóçÏ'kkk’J¥D{÷îU룃ééé´fÍòòò"äëëK[¶li6EçÓ8J§üÂ¥ZVV}ùå—äææFÈÁÁ�fÍšE§OŸnò—áVVVRLL }ôÑGäììLÈÉɉ-ZÔ¬~™¿J^^­]»–¼½½ YYYÑ?þñŠŒŒT‹s·YYY´uëV:t(éèè�T*¥Aƒѯ¿EÖJ¹IDATþÚl¿ä^$!!�fÍšE€|||hÁ‚§?*®]»F‹/&???@&&&ôþûïS\\œØ¡©Lyy9íÙ³GQ|ëëëÓ¨Q£h×®]j1“rRR­^½šzôèAd``@'NlÒWî)çOiÖIˆˆ °Ë—/ãçŸÆÞ½{qóæM¢gÏžèÛ·/zöì OOOÈd2¡Ãx¡ÊÊJ$$$àäÉ“ˆŠŠÂ‰'�——Œ1!!!èÔ©“hñ57oÞÄÞ½{±wï^ÄÆÆBGG]ºtA`` z÷î�:@WWWÔ³³³qîÜ9?~Ç�G\\455ѧOŒ1C‡…¹¹¹¨1Š­ªª 'NœÀÞ½{ñ믿"==VVVèÝ»7Ñ£G¸¹¹ACCC´‰wîÜÁéÓ§¹LOO‡……† †#F 00ššš¢Å(¶ŒŒ üòË/øùçŸqòäI€ŸŸúôéƒÀÀ@øûûÃÈÈHÔSSSñÇ ** QQQHLL„‘‘‚ƒƒ1bÄ 0@ô}†X8�²^%…ËÓnÞ¼©x#Nœ8�ÜÜ\èèèÀËË ~~~ðññ�««+\]]agg§Ô(áÁƒHLLÄíÛ·qõêUÄÆÆâêÕ«(..F‹-гgOôéÓ}úô�‡‡‡ÒúnNîß¿�¨¨(Å—Jjj*d2ÜÝÝáëë‹: M›6pqq�££#´´´”Ú~~>‘˜˜ˆ„„ÄÆÆ"66÷î݃D"�§§'ѧOôìÙSô@S%—Ë«Èã™3gPTTCCCøøøÀ××^^^puu…‹‹ lmm•Czzºâó�ØØX\¾|ùùùÐÕÕE—.];óŽ;B*•*=u—““ƒèèh?~QQQ¸uë$ \]]áçç___´iÓ®®®pvv†ŽŽŽRûôèqçÎ\»vMñyÌÌÌ„L&C§N�9ìÒ¥ ´µµ•Ú¿ºãüÕ›ê —§Éårܸq—.]R¼YqqqÈÏÏhkkÃÉÉ VVV°´´„¥¥%ÌÍÍ¡¯¯ccc€žž´µµQ^^Ž¢¢"O¾ØŠŠŠ����‡"33YYY¸{÷.JKKhß¾½âÃ×מžž¼cl€»wïÖÈá•+W�••�J¥°··‡­­-ÌÍÍannkkkÂÈÈR©ššš000ðäC¥¥¥(..ÆÃ‡‘���ììldeeáÞ½{5ÚvppPäÏ××~~~¯ýQ•†ª¨¨PóÕ·ëׯ£¸¸ ££XXXÀÊÊ 077‡žžžâ󨯯---TTT °°À“Ïcqq±"�xøð!îÞ½[£ívíÚ)òØ¡Cx{{+}'ý:HKKÃÅ‹‹K—.áÊ•+xðà@"‘ÀÎÎvvvŠüYYYÁØØ†††�Édµ~KJJ9¬þLfddàÞ½{ÈÉÉhjjÂÕÕµÆgÑ×׆††â¼jŠó÷Jâ./’••…;wîàÎ�;¸{÷®¢ø¨~ÃKKK )**Byyy�dµhÑ:::5kaa'''¸ººÂÍÍ VVVb¾Äf/??wîÜQIOOW|h²²²PPP€ÂÂBTTTÔ(:[´h‰D===èèè(Š�ê[«V­àââWWW899)ýh{^ZZš"—IIIŠV"%%%ÈËËPûçÑØØºººŠZXXÀÒÒŽŽŽ5ŽæH$1_f³VTT¤øU}çΤ¥¥)ò—™™‰üü|Åçñé¢óéÏãÓ9¬Î£ƒƒƒ"‡ŽŽŽ¢žòoÎ854ÍÂ…1Æc ãçç‡þýûcÉ’%b‡"„õâ�ÀcŒ1Æ«'.\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1Æcjƒ ÆcŒ© .\cŒ1¦6¸paŒ1ƘÚàÂ…1ÆcjCBD$vŒ1Æ«¿#GŽà£�>BUU•â¾””èëëÃÜÜ\qŸ¾¾>~ûí7XXXˆ¦2­—‰cŒ1Ʀªª W¯^}îþ‚‚ddd(þ¯­­ U†&>UÄcŒ©©~ýúÁÄÄä¥ÛÈd2 2†††*ŠJX\¸0ÆcjJ&“a̘1ÐÔÔ|á6UUUx÷ÝwU•°¸paŒ1ÆÔؘ1cPQQñÂÇõõõ1`ÀF$,.\cŒ15ÖµkW´lÙ²ÖÇ455ñöÛoC[[[ÅQ ‡ ÆcL�I$„……Õzº¨¢¢ï¼óŽQ ‡ ÆcLͽèt‘¹¹9zöì)BDÂáÂ…1ÆSsÞÞÞhݺu�û455©T*RTÂàÂ…1Ækž=]TQQ�1cƈ‘0xæ\Æc¬HLL„››ª¿Öííí‘’’‰D"rdJµž�¸0ÆcÍ€‹‹ :tè‰DMMMŒ?¾¹-øTcŒ1ÖlŒ;À“ÓD£G�9aðZEŒ1Ƙš),,DNNrssQ\\Œ’’€±±1€'§‰’’’�šš 022‚žžLMMaff¦ÖóºðÆc¬ ©¬¬ÄíÛ·qóæM¤¤¤ )) ÉÉÉHIIAff&rssQVVÖ¨>ôõõaff;;;888ÀÑÑprr‚‡‡ììì”ôj”n=.Œ1ƘH***pùòeÄÄÄàêÕ«ˆ‹‹Ãõë×QZZ °¶¶†£££¢°°±±�™™™âȉ™™ GPôôôÿ.**Byy9€'Ghž>J“““ƒììlÀÔÔ^^^hß¾=|}}Ñ¥K—ç.· .Œ1ƘªTVVâ�?þÀ±cÇpæÌ\¸pÅÅÅ077‡���¢XðòòBÛ¶m¡«««Òørrr�k×®!..Nq+))�••ºté‚îÝ»càÀ�pwwWilÿÃ… cŒ1&¤Ç�ãÀ�8pà"##ñèÑ#¸ºº¢{÷îèÖ­ºté"VP'ååå¸téΞ=‹Ó§OãÌ™3ÈÉÉ�‹‹ ‚‚‚0xð`ôîÝ2™J†ÍráÂcŒ)[UU¢££±mÛ6DDD ´´o¼ñ‚ƒƒ ±Cl0¹\ŽË—/ã·ß~Ã� „„„ ,, ݺu²{.\cŒ1eIKKÃÚµk±yófäää G� EHHˆâŠŸææîݻؾ};vìØ�[·nÁÓÓÓ§OGXX˜§º¸paŒ1ÆëêÕ«X±bvïÞ 333L™2ãÆ�C«V­ÄM¥Î�;‡Í›7cÇŽ000ÀäÉ“1}útXZZ*« .\cŒ±†ºsçÂÃñk×.xyyáÃ?Ę1c ¥¥%vh¢ÊÊÊÂúõë±~ýzaÖ¬Y˜={6ŒŒŒÛ4.Œ1ÆX}åååá“O>Á·ß~ WWW,\¸o½õV³œb¿1Š‹‹ñõ×_ãË/¿„L&Ã_|�É“'CC£Á÷sáÂcŒÕÇþýû1eÊÈår,^¼cÇŽ…T*;¬&-77K—.ÅW_}lÞ¼¹¡WRñ"‹Œ1ÆX]!44C‡E¿~ýpýúuŒ?ž‹–:055ŲeËð矢¤¤:tÀêÕ«Ô¯UÄcŒ½Bbb"Þzë-dddà÷ßÇ€ÄI-y{{ãüùóX²d fÏž�‹/bÓ¦Mõºúˆ�¸0Æc/qæÌtêÔ ZZZ¸xñ"-�$“ÉŽƒâСCèÞ½;²³³ëü|.\cŒ±8{ö, „^½záôéÓ¯ÝåÍBzóÍ7qáÂ|÷ïßGLLÌsÛöìÙƒmÛ¶!55ÐÑÑ�½½=ìííñðáC¬\¹R¨—T«�?þ6l@bb"LMMkÛ„' cŒ1ƪüüüàì쌟þYìpj())�­­-=z„º|uß»whÓ¦ þúë¯�=|ø£F�Âýû÷±cÇøûûC"‘@.—cçÎ�˜1c† †-[¶õrjUXXWWWŒ7_~ùem›ð©"Æc¬ÚáÇqåÊ,X°@ìPž£««[¯Å _t”\.ÇСCqõêUœ?�;wV,U ¡¡�ÐÐPìÝ»EEEJ‰»> 0wî\¬]»ö…ýsáÂcŒýODD:wî ±CLDDbbb0wî\˜™™ÕºM¯^½0räHGöDXXÊËËqøðáZçÂ…1ÆûŸß~û C‡¬ý?ÿü�;wÆ´iÓ™L†‚‚lܸ‰Dqä#??+W®¬qßÓnß¾�àà`˜˜˜ S§NˆŽŽ®s €>}ú¼t»#F(þ}ëÖ-„„„`Μ9 C÷îÝWç>ëÃÌÌ ]»vÅþýûk}œÇ¸0Æcrrr`nnލ¨( ÒGëÖ­‘���œœH$¼ýöÛX³f ,--áââ‚»wïÖ¿òì}îîî¸yó&f̘�   ܽ{³fÍBii)._¾ //¯ýI$’çÆ¸têÔ /^D^^Œ��ë·››är9QQQ ØÙÙáÚµkJxWž÷ÑGáĉ¸p᳭絊cŒ1<¹Z¶l)X�=£G�ðõ×_cúô野Ž@SSó¹ík»,X###@ii)fΜ‰•+WbëÖ­¯Œ¡zQÈâââ:.S¦L����âùfff¸yóf�žÛ¶¶¶xðàA­�ñ©"Æc @AA( !¬_¿˜9s&üýýQXXØ þž~ΰaÃ7nܨÓs«Çï$$$Ô¹¿Y³f!88ß|ó -Z„²²²W^’ÝÆÆÆÈÏϯõ1.\cŒ1ÖÖÖ€ŒŒ Áú Á•+Wп\ºt Ý»wÇ÷ßߨ6­¬¬¼ø*¢gUÏMsîܹ:÷qá´oßÎη��Aý­‡ôôtÅžgqáÂcŒáÉ)"‰D‚û÷ï ÖǧŸ~ DFFbçÎ�¨¬¬Äüùó@1·¬¬ À“Ë–«gÉ}ÙpÔêx\§BCCáëë‹Õ«W#--­ÖmJKKkTcÇŽEEE¨ˆíUq5ƽ{÷`kk[ëc\¸0Æcx2OŠ��Ž;&X+V¬À£G�<9úbdd¤ø‚vww,\¸·o߯êÕ«ELdd$ªªªÅMnn.€'…Ãþó 2ï½÷^�¾Š‹‹UUU5î—J¥Ø¾};tttЭ[7DDD(Nûãøñã RÄ<9’ššŠ£G�bÇŽÈËËðäHŒ…^TTºvíZëc|UcŒ1ö?_|ñ6oÞŒ{÷îÕzrcI$tèУF�B||<?~Œ5kÖÀÉÉ ·nÝBXX®]»†®]»bíÚµ˜8q"„·Þz §N�Â×_�‚‚¸¹¹AWWmÚ´ÁäÉ“ƒn ::?þø#¾ýö[Èd2,Z´ýû÷‡���b›‚‚¬[·§N�Â�7 ¯¯™L†   Ìš5«Æ/ß|ó æÍ›‡6mÚà믿Æùóçñù矣G�øî»ï^8LCÄÇÇÃËË çÎ�CçÎ�Ÿ}˜§ügŒ1ƪýõ×_h×®~úé'Ñ&`{ÝM˜0§N�ÂíÛ·k+¹paŒ1Æž† .àúõë�ÉxÖUºyó&<==±uëV¼óÎ;µmÂ… cŒ1ö´»wïÂÃÃóçÏW œe«¬¬DŸ>}�ŸŸ�K—.AC£Öa¸¼È"cŒ1ö4ggg,[¶ Ÿ}ö™ uYMŸ}ö.\¸€ÿþ÷¿/*Zðà\Æc¬V!!!8sæ Nž<‰6mÚˆN³¶sçN„……aãÆ�˜4iÒË6å#.Œ1ÆXmþûßÿÂÙÙ���¸uë–Øá4[?þø#ÆŽ‹Ù³g¿ªhÀó¸0Æcµ244ÄáÇѪU+ôîÝ»¶ÿX#­_¿aaaøðñlÙ²:=‡ Æc쌌Œ oooÅœ%¬ñÊÊÊ0iÒ$¼ÿþûøüóϱ|ùò:?— Æcì%ŒŒŒpàÀÌš5 “&M¸qã3ײú‹‹‹C—.]ðóÏ?cß¾}õ¾r‹ Æcì444°xñbüúë¯8zô(Úµk‡½{÷Š–Z)++Cxx8:vì]]]üùçŸu^_éi\¸0Æcu4dÈ$$$`È�!9r$úõ뇋/ŠV“FDسg<==±jÕ*üûßÿÆÉ“'áææÖ ö¸paŒ1ÆêÁØØ7nĉ'PXX¼ýöÛ¸qã†Ø¡5)D„C‡ÁÏÏ£G�F×®]‘��€9sæÔXW©¾¸paŒ1Æ G�ˆ‰‰ADD®]»OOO 8GŽÁë>>4h ÿ—.Ѝd\¸0ÆcªvãÆ E1pæÌ$%%lmmáéé ///´mÛNNNptt„���`GgJJJ�””„äädܽ{×®]õk×�üü|H¥Rx{{£[·nŠ›��� ±Ô.Œ1ƘØÒÓÓ‡¸¸8ÄÇÇ#>>ýõJKK2™ ¶¶¶hÙ²%LMMaff¦¸ikkÃÈÈ ­­ ===ÀãÇ�!—Ë!—Ëñøñc””” ''Gq{øð!ÒÒÒ�™™©ˆÃÔÔíÚµƒ§§'¼½½Ñ¾}{´o߆††ªSjÇ… cŒ1ÖTedd 99ÉÉÉHIIAff¦¢ðÈÍÍENNÊËËñøñc@ii)JJJ¸}û6–/_ŽÍ›7ÃÖÖcÆŒÁG}„V­Z±ŽÈÍÍŶmÛ°aÃdffbذaX´h\\\XGã8}Á/Òe˜g®Câãã1räHˆÅbüþûïèÙ³'ëH*qæÌŒ= …{öìA�=XGâ8]ÃçÌuž}ûЯ_?xyyáÂ… zÓÈÀÏÏ.\@§N�зo_8p€u$ŽÓ9¼™ë€­[·bøðá?~<öîÝ +++Ö‘TÎÚÚû÷ïǘ1c0tèPüú믬#qœNá—ók¹}ûöaòäɘ;w./^Ì:ŽZ‰D"lܸÖÖÖ˜4i¬¬¬Ä:Çé>g®ÅΞ=‹€€L˜0?þø#ë85uêTlß¾ÑÑÑèÖ­ë8§íøECÚª´´�;w†‹‹ <¨Sg«¨‚\.Ç[o½…Û·o#%%EåËpœžá@µÕ'Ÿ|‚‡bëÖ­�®‘�¦\¶mÛ†ÒÒRLŸ>�uŽÓz�¯Kè€ÄÄDlß¾6l€�� ë8ÌØÙÙaÆ ضm’’’XÇá8­Æ§Y´�ŸŸ$ N�:Å:J%%%Ê¥q5Åßß�ÑÑÑ—ãtŸfÑ6 ˆ‹‹Ã²eËT²½cÇŽaÔ¨Q7n®\¹¢üzLL  �@ìÛ·¯Î6ªªª°dÉøøø iÓ¦*Éõ"–.]Š˜˜$''k|lŽÓ|Ï\Ë|ðÁ8{ö,.]º¤²mVUUÁØØ(**ª3Ÿ““{{{dggÃÞÞ¾ÞmTVV¢eË–(..f²ŽJ§N�àë닟~úIãcsœà{æÚD.—#""&LPév�ŒŒ<šƒ®ï`êãyù†nùfllÌtþ~ܸqؽ{7 ³ §Íx3×"—/_FII ú÷ï¯ÑqE"Q­ÿj£¾}û¢¸¸iii¬£pœVâW€j‘„„XXXÀÍÍ�u<|øóæÍCyy9lll —ËQQQQë9<ÀâÅ‹!‰P]]�ÔÔTxxx`þüù*_rÀÓÓfffˆ��‡§§§J·Íqú€7s-’‘‘gggæç•Ëd2¢cÇŽØ´inݺ…o¿ýVùœ²²2tëÖ £F�ÂW_}ÈÏχŸŸöíÛ‡””•žõ"‰Ð®];ddd¨l›§Oø4‹),,dr¶È“6lØ€¤¤$Ìœ9�Ю];899)ŸóÍ7ßàÆ�˜:uªò1Ì›7ÿüó–.]ªò\Íš5ãkžsÜSðf®E>|cccµl[,?õà¡L&ƒD"Qþýرc€¶mÛÖzÞ¿?1ÄÅÅ@�ËìýýýàÍ\‹XYY¡¸¸X-ÛnÓ¦ >æææèÞ½{ƒ `éªÈÈHôèÑÖÖÖˆ��G»víXGâ8�›¹Ž°µµÅéÓ§ñöÛo#((³fÍBee%ëX¯¬²²3gÎÄàÁƒ„S§N1½q4Çé*~Tmß¾}ôlllðÃ?à­·Þb饌ÄÄD•¯ÙÎq�Ÿ3×3çÎ�ÃÆ�±sçNH¥R`ذaxûí·ÑªU+�dÈÌÌÄ¡C‡ðÇàôéÓ066Æûï¿�©S§¢K—.ÉÀq� ¿T_egg£oß¾pppÀÙ³gQ^^޶mÛÂßß={öDÇŽáææ33³W§¬¬ iii¸téâââƒ;wîÀÔÔîîî8wîŽ9‚þýû«è•qWÞÌõaĈ8sæ RRR`mm�„„ÄÄÄ ::III¨¨¨€@ @›6màèèØÙÙ¡yóæ°´´„@ €¥¥%€GË JJJpÿþ}äææâîÝ»ÈÈÈ@FFˆ¦¦¦ðöö†¿¿?z÷î�=zÀÐÐ#GŽDll,Ο?�–-[2® Çé-ÞÌõÑŠ+ðÅ_àĉð÷÷¯óu"Â?ÿüƒÔÔT¤¥¥!++ ÙÙÙÈÉÉAaa!áÍ\_͢ꪪàëë "B||<Œ��YGzn7nDhh(þøã 2„uŽÓu{Ĭp//44wîÜÁùóçuª‘ÀÔ©S‘œœŒ‰'ÂÓÓÎÎά#qœNã{æ:jãÆ�øðÃqðàA 8�uœ—RUUTUU!!!&&&¬#qœ®â@uÑÅ‹1sæLÌ›7Og9!""¹¹¹øàƒXÇá8�Æ÷ÌuLqq1ºté'''üõ×_‰D¬#½²'Nà�7ÞÀ÷ß�éÓ§³ŽÃqºˆï™ë"„  “ɰsçN½häÀ£5Ù-Z„Ù³g#&&†uŽÓIü¨Y¼x1>ŒS§N¡yóæ¬ã¨Ôܹs‘’’‚‘#Gò9î%ðiqòäI 0kÖ¬ÁG}Ä:ŽZð9î¥ñËùuÁÝ»wáåå…¾}ûbçÎ�¬ã¨_a‘ã^ oæÚ®¦¦���(((@rrò+ßHð9î…ñ ÚnÖ¬Yøûï¿ñçŸ6ŠFï¼ófÏž�ÐÐP¤¤¤°ŽÃq:�ï™k±Ý»wã½÷ÞÃöíÛ1fÌÖq4Н°Èq/„O³h«ÇsÇ'NĪU«XÇa¢°°]»vEûöíqäȽ9“ãÔ€7smT^^ŽîÝ»ÃÜÜÑÑÑ�ú¬Ž¤¤$øûûcîܹøê«¯XÇá8mÅç̵Qhh( °gÏžFÝÈÀÛÛkÖ¬ÁÂ… ±wï^Öq8Nkñ‹†´Ìš5k°cÇ9r¬ãh¾Â"Ç=ŸfÑ"gÏž…¿¿?,X€Ï?ÿœu­ÂWXä¸ñ9smQTT///xxxàÀ� ù Ø“233ѵkWôë×;vì`‡ã´ Ÿ3× …£F�lÛ¶�7ò§pttÄÎ�;ίå¸'𮡾üòKDGGã�?þàçS?_a‘ãêǧY;t肃ƒ±qãFLž<™u�@D9r$bccù ‹÷Ÿ3g)33^^^xóÍ7ñÛo¿±Ž£Sø ‹W oæ¬H¥RøùùA*•"11Mš4aIçð9Ni?Ïœ‘O>ù×®]CRRoä/ÉÅÅÛ¶mÃСCáååÅWXä5~”�ßÿ?ÿü3¶nÝ WWWÖqt_a‘ãáÓ,vùòeôèÑŸ|ò ¾ùæÖqô_a‘ãøœ¹F•••ÁÛÛ¶¶¶8~ü8Äb>Ë¥*|…E®‘ã i a„ (..ÆŽ;x#W±¦M›b÷î݈‰‰Á¢E‹XÇá8�ãÍ\Å.^¼ˆŒŒŒ:�ûí·Ø¿?ÂÃÃaooÏ ™þã+,r�ŸfQ!"‚½½=ÊËËñÛo¿aðàÁ€øøx`Ù²e˜={6ã”úoòäÉøã�?�œœ\g…Åììl˜››7š[ðq�ŸfQ¥³gÏ"77xç�w†{÷îaĈ8p fÍšÅ:b£°nÝ:´oßC‡EEE…òñÝ»w£}ûöü*§—øž¹ Íš5 ëׯGuu5@$ÁÖÖ†††¸xñ"ÌÍÍ'l<þ½Â⯿þа°0¬Zµ D333ð«F9}ÂÏfQ"BË–-‘““Sëq±X üùçŸèÓ§£t�STTgggܼyr¹ ‰·ß~›qBŽS>Í¢*gÏž­ÓÈ@&“¡¼¼ýû÷ÇòåËÁwj޵µ5,,,pûöme#ý‚ݵkÃd§z¼™«HDDÄS?¶Ëår( üßÿýÆŒ£ád�Ó¦M›Ð£GæÎ�˯,ÔRD„ÿþ÷¿˜5kª««•¿€ùå„„„(ŸWYY‰ääd\ºt ©©©HKKí[·^ø}ddd¸¸¸ÀÃÃîîîðòò‚››Ûsí(pÜ Ðíf~éÒ%;v qqqHLLDnn.„B!Z·n ¸ºº¢mÛ¶°±±�½½=š7oKKKA$)ç±KKK!—ËQUU…’’äçç#''yyy¸sçÒÓÓqíÚ5dffB¡P E‹èÑ£œ��ñí·ßÂËË ¿þú+ÜÝÝW„{wïÞÅ´iÓpèÐ!@¿~ý°páB>|§OŸFrr2¤R)¬­­•MØÅÅ-Z´€½½=lmmamm ¡P¨üT÷ðáCH¥RTUU¡¨¨÷îÝCNN²²²påʤ¥¥!==ÕÕÕhÞ¼9zõê…ÀÀ@ÃÑÑ‘qE8= [Í\.—ãØ±cØ»w/Ž9‚»wïÂÆÆ={ö„¯¯/zôè///µÝSóáÇ8þ<���€øøxäçç£U«Vxë­·0dÈôë×�ßAG,^¼K–,AUU ]»v@ï޽ѻwo•7Y™L†‹/"&&ÑÑÑˆŽŽFii)ºté‚aÆaܸq|ydîeíé€Ë—/ÓìÙ³©E‹$¨{÷îôõ×_Srr2Éårf¹är9%%%ÑW_}Eݺu#�@@ööö4gÎJKKc–‹{º²²2Z¿~=yzzrww§!C†ÐÅ‹5žE*•Ò‘#GhÊ”)Ô¬Y3‹Å4dÈ:vì˜Æ³p:/\«›yll, 4ˆµjÕŠÂÂÂèúõë¬c=UFF}óÍ7äììL¨gÏžtàÀR(¬£5zeee´zõj²³³####1bEEE±Ž¥$•J)<<œúõëG�€:vìHáááü½Ã=/ílæGŽ¡N�:êׯ9rD§ÞÔ …‚:D}úô!Ô¹sgú믿XÇj”är9ýøã�Ô´iS277§yóæQaa!ëX :þ<“@ oooJJJb‰Ó~ÚÕÌÿþûoêß¿? ÁƒÓ… XGze)))DhÀ€téÒ%Ö‘�óçÏS·nÝH"‘ÐgŸ}¦õMüI)))@B¡�¦M›FÅÅŬ#qÚK;š¹T*¥ùóç“D"!oooЉ‰aIåNŸ>­l, , êêjÖ‘ô–B¡ åË—“D"!JMMeé¥) Ú¾};ÙÙÙQëÖ­)..Žu$N;±oæ©©©äééI&&&ôÃ?0= ©nr¹œÖ¬YCMš4¡Ž;òƒ¤jPTTD ‰DB+V¬Ð©é¹†Ü¿ŸÞ~ûm‹Å´bÅ Öq8íö™ïÝ»—ÌÌÌÈÏÏ�nß¾Í2ŠFݼy“|||ÈÜÜœöïßÏ:ŽÞÈÌÌ$wwwjÕª•^Î3+ Z¹r%‰D" %™LÆ:§=Ø5óo¾ù†M›6­QN9H¥Rš|8ÕÔÔ°ŽÃi6Í|áÂ…$híÚµ,†×*«V­"�@@Ë–-cEgÝ»w�œœœÈÛÛ»Ñ$Œ‰‰¡&MšÐĉõf*‰{%šoæË—/'¡PH7nÔôÐZkýúõ$èûï¿gEçTTTP§N�ÈÕÕ•îß¿Ï:ŽF:tHy@�kô4ÛÌ>LB¡�Ö¬Y£ÉauÂwß}G"‘ˆŸ�þ‚¦M›FVVVtçÎÖQ˜Ø°a …B:uêë([á[›%##�;wFpp0¶nݪ‰!uÎØ±cqøða\¸p�/¾ô"##1xð`ìÙ³Æ c‡™#F 11—/_~®¥|9½¤¹…¶‚ƒƒqëÖ-œ?FFFšRçTVVÂËË ...Ø·oë8Z­ººnnnèÑ£~ûí7Öq˜*..†‹‹ Æ��+V°ŽÃ±±G#w:pà<ˆõë×óFÞccc¬[·û÷ïW.ÏÊÕïÇĽ{÷°lÙ2ÖQ˜³²²Âüùó±víZddd°ŽÃ1¢‘f¾páB >¯¼­?ÿü#FŒ€@ €@ À©S§žúܸ¸8åó† ÖàsµEß¾}1tèP,\¸�u­¥P(°jÕ*„††¢U«V*ÙfTT¨|¿"00]»vEpp0~þùgH¥R•Œ¥S§N…�� Ö¯_Ï: ÇŠºgåãââ%&&ªl›€PPPÐSŸ÷Þ{±1 œœ•�¯ngΜQyÍôÉ_ýE(==]¥Û½{÷. 6mÚ(“Ëå´ÿ~rrr"ggg­^`Á‚dkkÛ(¯Ûà(\í{æ[·n…——ºw﮲m>¾ù„¯¯/<ˆ7nÔyNNNŠŠŠ”íììT6¾ºõìÙ�:u–-[XGÑJ»ví‚��\]]UºÝ–-[ •� …BãÌ™3(//ÇàÁƒQYY©ÒqUe„ ÈÏÏ׉O œê©½™Ÿ8qƒ R˶g̘"š5kê|mÓ¦M U˸š0hÐ œ>>{øðá …8vì˜ò±§½�ˆû÷ïÇ”)Sààà€ââb„„„ iÓ¦ðððÀ¹sçÔ’Ñ×× jÙ6§åÔ9‰súôi@ùùù*ßöãèß~û- åË—+¿¶sçNúöÛo‰ˆÈÅÅ…ê{™ÎÎÎäääDDDÕÕÕdaaAîîîTUUEqqqʹö¥K—RTTMš4‰ŠŠŠ¨wïÞ4fÌåꎛ7o&tàÀ•¾¾œœ —Ë¿Šsçε-Ì€\\\žúu;;;²¶¶Vþýiï#…BAYYYdbbBhñâÅtçÎÚ¾}; oooµä_²d 9;;«eÛœVSï {öì!¡P¨–ÕÝ7èââb211!å�ŸþýûSQQ=½™¯\¹’vìØAD�r999‘X,V~½C‡ Ö V®\IèêÕ«ÊÇjjjhóæÍÊñT¥¦¦†„B!EDD¨t»ºîèÑ£€}ààà |L,c„ *|EÿÛ®¡¡!***T¾m]öøà£±±±ÆÇ®®®¥f¬Ë IDATF^^úõë§|ìyßGÿþ»••òóóÕ’ÑÔÔ”¿g)µÎ™7mÚ•••j?ú?}út¬Zµ ëׯÇôéÓŸù=IIIðôô„““æÏŸSSÓg~O^^Ô{öŒª=|ø•••hÖ¬™ÚÇÒ%�Iÿû‰¦œ|ø^Q]¹¹¹þWCM›îß¿¯Ñq¥R)>ÿüs¼þúëµv^æ}¤N÷ïßWÖˆk\Ô:Íâîî‰D‚¤¤$´iÓFeÛ}ü5//ööö€™3g"22~øa­ç>þÈYYYYë£yNNJKK…üü|”””x´§eoo�ªª*@yy¹rokîܹøý÷ß�~ýúaذaÈÍÍE~~>~úé'•½>8{ö,$I­_LÜ£_ÜFFF¸páÜÝÝUºí‡€òßþ±””̘1ÅÅÅ8tè$‰òkÏû>""å”KYY ¦¦¦Ö¶T!%%žžž*Ý&§#Ô=+ïããCS§NUÙööíÛGƒ "ôöÛoÓñãljèÑ�¥!C†(¶¦¥¥Ñ矮¼RtĈtòäIåvÖ­[GæææÔ­[7JHH Õ«W“¥¥%õíÛ—¦OŸ®ü¾>ø€RRR”ßwéÒ%0`YZZ’½½=}úé§TRR¢²×÷Ø|@={öTùvõ���…††ªt›±±±4qâDå¿{ïÞ½iÀ€DC‡¥uëÖQYYY�ï{Úû(88˜¾þúkåö-ZD%%%´jÕ*åcaaaôðáC•½…BAÖÖÖ|‰éÆIýKà.Z´k×®EVVV­+븧«ªªB«V­0cÆ |ñŬãh�ùóçcëÖ­¸sçŽZ®ëšèèhàòåËððð`‡Ó,õ¯š8yòd}úPAA‹Záþýû@ÖÖÖtþüyÖqt–\.§Ñ£G“™™Y­ Ãô•B¡ Ù³g“H$¢½{÷²ŽÃiõ.�Û� .�££#999Ñ¥K—XÅ`æâŋԦMjÓ¦ ýý÷߬ãè¼êêjzï½÷ÈÐÐ�vîÜÉ:ŽÚH¥R5jðžÜ¿±kæDDyyyäïïOMš4¡Õ«W+oø Ïär9}ÿý÷dllL���tÿþ}Ö‘ô†\.§Y³f‘@  °°0½»±ñ�;wÈ××—ÌÍÍ)**ŠuN»°mæD�ö¨,X@‰„zõêUëÆú&==�zöìIôõ×_SMM ëHzé—_~!êÑ£ݼy“u•سgYYY‘»»;¥¦¦²ŽÃiöÍü±‹/RçÎ�I"‘Ї~Hyyy¬#©Lnn.…††’X,&///>­¢W®\¡N�:‘±±1-\¸�ªªªXGz)·oßV.,7eÊ•.ÌÅéíiæDD2™Œ~þùgjÙ²%™™™QXXegg³ŽõÒîÞ½KÿùÏÈÔÔ”hóæÍ�b*I[TWWÓŠ+ÈÔÔ”œ��iûöíj¹…¡:äççSXX‘››�:uŠu$N»iW3¬¢¢‚–-[Fvvvd``@!!!”��À:Ös‹��§±cÇ’D"!{{{Z¾|9UTT°ŽÕheeeQHH‰Åbrqq¡-[¶híU£™™™4gÎ255%KKK’H$´iÓ&Ö±8í§�Íü±ªª*úå—_ÈÓÓSy£Ýŋӭ[·XG«ãÆ�´páBå |;vìH[¶l!©TÊ:÷ÿݸqƒÆ�OdmmM3fÌЊùçêêjŠŒŒ¤   ‰DdggGß}÷•––Ò‚ H(ÒØ±cù×ínæÿ–œœLÓ§O§æÍ›ruu¥Ù³gÓ‰'˜¼ÉËËËéøñã4kÖ,rqq!dkkKŸ~ú)?g\ËåææÒÒ¥K©mÛ¶Ê÷Ò_|AÉÉÉ›†)--¥ýû÷SHHY[[“@  ¾}ûRxxx�€ÈÈH²²²¢Î�;kåŽ §4·6‹ªÈd2ÄÆÆâðáÃ8|ø0®\¹±XŒŽ;ÂÇÇ]ºt�››\\\`ii©’1‹‹‹qíÚ5\¹rçÏŸGBB._¾ ™Lwww¼õÖ[xë­·àçç±X­wâãTH¡P`äÈ‘ÈÍÍÅÝ»w‘‘‘ øùùÁÏÏ^^^ðððPÞšðeÉd2ܸq©©©HLLDll,.\¸…B :C‡mðÖŠ7oÞİaÃp÷î]üþûïxóÍ7_)§wöè\3RVVâãã‘��€³gÏââÅ‹Êeìììàää„æÍ›ÃÞÞ666033ƒ™™€ºwz/++CYYòòò�““ƒüü|ܾ}[yCjccctêÔ =zô€��|}}áààÀàUsª°k×.Œ5 »wïÆˆ#�ššŠèèhÄÄÄàÌ™3¸wïÀÚÚ:t€��ZµjXXXÀÐÐMš4�¡¡!ÊÊÊ “ÉPVV†ÒÒRdee!//™™™¸~ý:ª««!‹ñÚk¯¡wïÞð÷÷‡¿¿ÿ ]†_UU…�>ú[¶lÁþó,]ºB¡FWäà´—î7ó') dddàÚµkHOOGff&ÒÓÓ‘””[[[TVV¢´´D¤¼ù®¥¥%ÌÍÍabb[[[ØÛÛ£yóæhݺ5\]]áââ‚Ö­[ó=qíÚ5tëÖ “'OÆ÷ß_ïs qùòe¤¥¥áæÍ›ÈÍÍEvv6òòòPZZ ©TŠŠŠ TWWÃÔÔ‰fff077‡‘‘²³³1qâD¸ººÂÝÝnnn*¹uâ¦M›ðÉ'Ÿ oß¾øí·ß`mmýÊÛätžþ5óú,]ºëÖ­Sîiq�[yy9ºwïsssDGGÃÀÀ@åcÄÇÇ£gÏž¸ví:tè òíŸ?LJ\.GDD¼½½U>§S4·.K±±±`ƒÓ“&MBAA"""ÔÒÈÀÛÛæææ8yò¤Z¶ß¥K$''ÃÕÕþþþøïÿ«–q8Ý¡÷Í\&“!>>½zõb…Ó«W¯FDD~ûí7´lÙRmãˆÅbøùùáÔ©Sj£Y³f8räæÎ�‹iÓ¦aܸqxøð¡ÚÆã´›Þ7ó . ´´þþþ¬£pŒ%&&",, K–,AÿþýÕ>^`` NŸ> uÎdŠD"|õÕWØ¿?<???ܾ}[mãqÚKï›yLL š5k777ÖQ8†òóó1|øp 0aaa³OŸ>ÈÏÏGjjªÚÇ4h’’’ —ËÑ­[7=zTícrÚEï›yll,üýýù-µ1…B�±cÇB,cëÖ­{/¼þúëhÚ´©ÚæÍŸäì쌳gÏâ�wÞÁ[o½…¹sçB¡PhdlŽ=½næD„3gÎðùòFnÞ¼yˆ‰‰Áü�¦M›jl\¡PµÎ›?ÉÈÈ¿üò 6lØ€U«VaРA(**ÒØø;zÝÌSSSQXXÈç˱ƒbùòåX¿~=ºté¢ññÏ›Ëår�Ž;eÊÄÇÇ#==¯¿þ:’’’4:>§yzÝÌcbb`nnŽN�:±ŽÂ1�‘‘�ñãÇcÔ¨Q˜8q"“ }úôÁƒpáÂ��ÍO_l\ô¾™ûùùA$±ŽÂiXUU† †–-[bãÆ�Ìr¸¹¹ÁÎÎNcóæOâ§/6zÝÌÏœ9çX©�?þ·nÝŸþ‰&Mš0Ë! Ñyó'ñÓ½mæ×¯_ǽ{÷x3o„~ûí7lÞ¼›7oF»víXÇA`` bbbP]]Í4?}Q¿ém3�‰‰A“&M˜ôâØ¹té¦N�йsçbÈ�!¬ãx4oþðáC­8ÉO_Ô_zÛÌcccáã㣶µ78íSRR‚¡C‡ÂÛÛ .dGÉÙÙ­[·f:ÕòoüôEý¤·Í<::šŸ_Þˆ&Mš„ŠŠ ìØ±Cën¨5Íü1~ú¢~ÑËfž••…ŒŒ >_Þˆ,_¾@xx8Z´hÁ:N���ˆ��Gee%ë(µðÓõ‡^6óÓ§OÃÀÀÝ»wg…Ó€Ó§OcþüùX±b…Ö~ëÛ·/¤R)âããYG©ƒŸ¾¨ô²™ÇÆÆÂÛÛ›é)iœfäææbÔ¨Qxûí·1cÆ ÖqžªeË–hß¾½ÖMµ<ÆO_Ô}zÙÌcbbøK# “É0räH˜ššbÛ¶mZ¿˜ZŸ>}˜]<ô¼øé‹ºKïšy^^®_¿®µ·9Õ Ã¹sç Öqž)00ÉÉÉ(--e¥AüôEݤwÍ<&&B¡>>>¬£pj´ÿ~¬Zµ ?ýô^ýuÖqžK`` är9Μ9Ã:Ê3ñÓu�^6óÎ�;ëÄž÷rnܸ���„††"$$„uœçfccwww­�7¯?}Qwèe3çóåú«²²#GŽ„‹‹ ¾ÿþ{Öq^˜.Ì›?‰Ÿ¾¨ôª™#55•7s=ŠÌÌLìÚµ †††¬ã¼°ÀÀ@\¼x¬£¼�úN_Ô¶sæ;½jæ±±± "øùù±ŽÂ©ÁO?ý„íÛ·ã·ß~CÛ¶mYÇy)ˆ��eå…ýûôÅÈÈHôìÙ“Ÿ¾¨Eô®™{xxhôÖ`œf$''cæÌ™øòË/1pà@Öq^š¥¥%:wî¬SóæO4h’““ùé‹ZF¯šytt4ŸbÑCÅÅÅx÷Ýwáçç‡yóæ±ŽóÊunÞüIüôEí£7ͼ¼¼.\àç—ë…B�Ñ£GC&“a×®]zqרÀÀ@\¹r¹¹¹¬£¼~ú¢vÑ›f™LÆ›¹žY´hŽ?Ž]»v¡Y³f¬ã¨„¿¿?$‰NOµü?}Q;èM3���E‡`ooÏ: §"'NœÀ¢E‹°fÍøúú²Ž£2&&&èÖ­›Þ4s€Ÿ¾¨ ô¦™óóËõKVVÞÿ}Œ9¡¡¡¬ã¨œ.žoþ,üôE¶ô¢™K¥R$''ó)=QSSƒ÷ßÖÖÖØ¸q#ë8jˆ[·n!##ƒu•â§/²£sͼ²²C‡ÅgŸ}†ÈÈH#11UUU|Ï\OÌœ9ÿý7öîÝ 333ÖqÔÂ××ÆÆÆu¦ZÊËËõâ¬~ú"¤c  …B@�€ìììÈÊÊŠÂÃÃ)77—uDîìܹ“…‡‡³Ž¢v}úô¡÷Þ{�öïßOŸ~ú)¹ºº’@  Í›7³Ž¦2•••4qâDFr¹œu$}®sÍ\¡P�D"!µþH$€Ú´iCS¦L¡¢¢"Öq¹põêU233£™3g²Ž¢6åååtôèQ #'''$ÈÀÀ@¹sò믿²Ž©r7n$8p ²Ž£�ÂDDì>¼{{{äää4ø±XŒëׯëìeßúlúôéèß¿?‚‚‚”�•——£{÷î077Gtt4 &T�wß}þù'd2 P]]]ïóŽ=Š7ÞxCÃéÔïüùó>|8är9"""àííÍ:’>Ù£ssæžyú¡H$Â_|Á¹JKKÃÚµkŒ¹sçB.—&Mš„‚‚DDDèe#CCCåë}Z#-•«�øé‹jÆú³ÁË2dˆrJåÉ?b±˜ÚµkGUUU¬crõX°`�ršL$Q¯^½háÂ…$ 騱c¬ã©Uqq1ÙÚÚ*�÷<íÏÝ»wYGU+™LF , ¡PHcÇŽ¥‡²Ž¤toΜˆè£�>ªwÞÿÎ1..ŽuDî)Ú·o_çX‡‰‰ M�:•u4�ˆŒŒl°‘ ’J¥¬cjDdd$YZZRçÎ�éÖ­[¬ãèºp�œfiÑ¢„ºÑÅb1fΜ©WW ê“´´4ܸq£Öc555¨ªªÂÿû_,_¾¤{‡p^È Aƒ0zôhH$’z¿nbb¢·ÓLOâ§/ª–Î6s™LVë1¡P[[[|ýõ׌RqϲgÏžz›˜\.‡B¡À矎àà`”””0H§9ëÖ­ƒµµu½;$ú²þÌózÞÕ¿üòK¸ººâáÇ RêÖŸ ^Æ‘#Gêýxªïs®ºîÉ)–§ý6lë¨j÷´éoooÖјyÚé‹û÷ï'�@@B¡�æÌ™Ã0¡VÓÍ9ó‹/Ö™w�8q"ëX\®\¹òÌ&.‰ÈÃÃþþûoÖq5b̘1uŽý 2„u,¦Î�;GmÚ´¡V­ZÑÙ³géÆ�djjª>>°³³k´ïÿ`äÈ‘8~ü8ˆ¨Á¦Þ¤I\½z­ZµzêsIÿÙ£ÖSÏœ9C¡¡¡dooO¨]»v4iÒ$úõ×_)##CåãÕÔÔPrr2­\¹’‚ƒƒÉ‚uíÚ•–-[FÙÙÙ*S›iKý% ½ÿþû¼þüýÿÜ iÛ¶môÆo�H$"‘HTïš6‰„ Pï6YýUžyYY­_¿ž<== uìØ‘¾þúkºté’ª‡z&©TJGŽ¡)S¦P³fÍH,Ó�!Côúâ"^¶xýU¯   ÁÆ.hÇŽDԨ믺f^VVF«W¯&;;;222¢#FPTT”ª6ÿʤR)…‡‡S¿~ýH PÇŽ)<<œ ëh*Áëϯ¿fÜ»w�Ö®]K>>>$H,²°° %K–4æú¿z3—Ëåôã�?RÓ¦MÉÜÜœæÍ›§õw9þ<“@ oooJJJbé¥ñú³ÅëÏNvv6­^½šÚ´iCÈÈȨ1×ÿÕšùùóç©[·n$‘Hè³Ï>Óú">)%%…H(Ò´iÓ¨¸¸˜u¤Âëϯ?[ÿ®hh(Ý»w�u¤¢âú¿\3W(´|ùr’H$äïïO©©©¯‚)…BAÛ·o';;;jݺµN¬…Îëϯ?[¼þõzñf^TTD ‰DB+V¬Ð¹9·§¹ÿ>½ýöÛ$‹iÅŠ¬ã<¯?[¼þlñú?Õ‹5óÌÌLrww§V­Zéì<[C ­\¹’D"…††’L&c©^¶xýÙâõoÐó7ó7n�ƒƒyzzêý= ÷îÝKÆÆÆ4|øpª©©a‡ˆxýYãõg‹×ÿ™ž¯™ß»w�œœœÈÛÛ[ç’¼¬˜˜jÒ¤ Mœ8‘ùG9^^Mãõgë%êÿìf^QQA�:u"WWWºÿþ«§Ô!‡"‰DB ,`–�ןן^¶^°þÏnæÓ¦M#+++ºsçÎ+‡ÓE6l ¡PH§N�b2>¯?¯?K¼þl½@ýnæ �@@* §‹†NÿˆÇëÿ¯?[¼þl=gýŸÞÌ¥R)µk׎F�­út:¦¨¨ˆš7o®Ñ›Éòúÿ¯?[¼þl=gýŸÞÌW­ZEÆÆÆ”™™©út:è‡~ ###�}Üãõ¯�ן-^¶ž£þõ7s¹\NŽŽŽ4kÖ,•9yò$ >\¹ÊÙ”)SèÌ™3 ~�··7}öÙg*_¤R)9::jdïDÕõoH\\}üñÇÊ›÷ߟ<¨öq_”¾Õ_Wêþ¯?[ÏQÿú›ù_ýE(==]ea***9::>×óß}÷]š7ožÊÆW… �­­-UWW«uuÔÿYZ·nM¨ªªê…¾O“{NúXÿ—­; ¼þl=£þõ7ó &����Êà •oWSîܹC�€þúë/µŽ£®ú7ÄÅÅ…€[ÝáöíÛäç秦Duécý_¦î¬ðú³õŒú‡×{'ÔØØXôïß_•·4Ò ­[·†³³3Μ9£Öqt¡þwïÞÅ Aƒpÿþ}��Éëϯ?[Ϫ�f^PP€[·nÁÇÇGíáê#—ËŽ��øûû+¿~ý:†ް°0Œ;½zõÂ¥K—@DØ¿?¦L™#$$M›6…‡‡Î�;§Ò|¾¾¾HHHPé6ÿ�uýÿ­²²Ë—/ǤI“еkWôë×—/_lݺW®\Ann.¦M›¦±L�¡þÚò^¯Oc¨ÿƒ0gÎÌ�;³fÍ€0kÖ¬Z7ÜnègC�¬ÿ“ûêçÎ�#tûöm•LÀsN³dddÔy®³³3999Quu5YXX�»»;) ÊÊÊ"@‹/¦;wîÐöíÛ y{{«ô5,Y²„œ��UºÍSgýRßÇÍÉ“'ך·ìß¿?ÙØØÐƒˆˆÍ´™¾Õ¿¾ºkË{½>ú^ÿÒÒRjß¾}­«.óòò¨}ûöÔ¶m[å¹ÞÏúÙP—ê_wÎüèÑ£@-¡ž÷‡_¡PÔyîÊ•+•÷ù“ËåäääDb±Xùõ:ÔúGQ(dccC*|�®È²²²Ré6ÿM�õoÈ“oêÄÄÄ:7Ï}ü'22’ˆØ4s}«}Í\[ÞëõÑ÷úþùç Î�.¶mÛFhΜ9Ïõ³¡. Ô?\üäžzee%ÀØØø¥>¨‚@ ¨óجY³P^^Žõë×£¨¨R©2™ì©ß#`ee…üü|•f355EEE…J·ùoÚPHNN†››ÒÒÒ˜æxRc¨¿¶¼×ë£ïõ�‹‹˜™™Õzüñ”o||<™ýl4Tÿ:sæVVVPk~HSòóóQSSSï×’’’àéé '''ÌŸ?¦¦¦N÷Haa!¬­­Õ¶}–õ‰DÊÿ/,,Ä?ÿüSïG.—k2V-úXÿ×О÷z}ô½þBᣖxçÎ�ZϱµµXXX0ýÙh¨þušyÓ¦M@£g)áÃ?¬óÆ~lܸq¨©©ÁÀ� …Bù}štÿþ}e�ÔA“õ///ǃ”oÓ¦�òÿ]]]•yþíÊ•+X·n�òïÿÞcÔ}¨Cu´ç½^}¯ÿã=ðC‡Õúž¬¬,@¿~ýžûgCª�i–öíÛÃÈÈ.\€»»»ÊBÜ»wˆòlû_IDATPVV…B¡ü üï豑‘„B!ÊÊÊ¥¥¥Êçäää ´´QQQÈÏÏGII €G{1ööö¨ªªðè ÿøcèãíÔÔÔ@"‘¨äu¤¤¤ÀÓÓS%Ûª�ºê_Ÿ�"??W®\Aaaa­³RŒ¶mÛbÑ¢EÈÎÎFŸ>}�žžŽ¤¤$DDDÚµk‡œœdffÂÑÑQ­YÓ‡ú7Tw@{ÞëõÑ÷úÿç?ÿADDÖ®]‹qãÆ¡E‹€õë×Ã××ü1ärù36Ô¥Áú×7“îããC¡¡¡*›´?qâ+¸¸¸P@@P‡ÈÀÀ€ÐÖ­[©¼¼œæÎ�«|îÊ•+éÁƒ´nÝ:277§nݺQBB­^½š,--)88˜¾þúkåó-ZD%%%´jÕ*åcaaaôðáÃW~ …‚¬­­iÍš5*¨ÊÓ©ºþO³víZrtt¤€€š>}z�ýóÏ?DVVVdkkK|ðåçç+¿>wî\²³³ÓتvúRÿgÕ]Þëõi,õ/--¥9sæPÿþýiÖ¬Y4gÎZ¸pa­«DŸõ³¡ϨýW€Î›7�´î€¬�>}šÐåË—Õ:¯ýxýÙâõgëõ¯¿™ßºu‹9rD½étÌØ±c©[·nj‡×¿~¼þlñú³õŒú?} ÜÞ½{Ó€Ô“Jeee‘±±1mܸQ#ãñúׯëϯ?[ÏQÿ§7óèèh öEutÅøñã©M›6[]�׿6^¶xýÙzŽú7|Û¸   rww§ÊÊJÕ§Ó! $‰è÷ß×踼þ�ðú³ÅëÏÖsÖ¿áfž™™IVVVôÉ'Ÿ¨6�)++£öíÛÓo¼A …B£cóúóú³ÆëÏÖ Ô¿áfND´sçN´k×.Õ%Ôr¹œ†Jvvv”››Ë$¯?¯?+¼þl½`ýŸÝ̉ˆf̘A†††tüøñWO¨CBCCÉÈȈbbb˜æàõçõg�ן­¬ÿó5s¹\N£G�&333:yòä«%Ô …‚fÏžM"‘ˆöîÝË:¯?c¼þlñú?—çkæD�ÖU~ï½÷ÈÐÐ�vîÜùr)u€T*¥Q£F‘���Æø4„ן-^¶xýŸéù›9Ñ£ß�³fÍ"�@@aaaj¿±«¦Ý¹s‡|}}ÉÜÜœ¢¢¢XÇ©ƒ×Ÿ-^¶xýôbÍü±_~ù…LLL¨G�tóæÍ—Ù„ÖÙ³gYYY‘»»;¥¦¦²ŽÓ ^¶xýÙâõ¯×Ë5s"¢+W®P§N�ÈØØ¸Î"4ºäöíÛ4hÐ @S¦LQÛ"EªÆëϯ?[¼þu¼|3'z4�µbÅ 255%gggÚ¾}»Î,Ž“ŸŸOaaadllLnnntêÔ)Ö‘^¯?[¼þlñú×òjÍü±¬¬, !±XL...´eË­½j+33“æÌ™C¦¦¦dccC«V­Òù¹7^¶xýÙâõ'"U5óÇnܸAãÇ�'²¶¶¦3fhÅü[uu5EFFRPP‰D"²³³£ï¾ûŽ***XGS)^¶xýÙjäõWm3,77—–.]JmÛ¶%äêêJ_|ñ%''kìcPii)íß¿ŸBBBÈÚÚšõíÛ—ÂÃÃI*•j$+¼þlñú³ÕHë. Rß� âââðçŸbïÞ½ÈÈÈ€……üüüàçç///xxxÀÞÞþ•Æ‘Éd¸qãRSS‘˜˜ˆØØX\¸p …>>>:t(†Zç^‹úŽ×Ÿ-^¶Yý÷¨µ™?)55ÑÑш‰‰Á™3g”÷µ¶¶F‡`gg‡V­ZÁÆÆ044D“&M`hhˆ²²2Èd2”••¡´´YYYÈËËCff&®_¿ŽêêjˆÅb¼öÚkèÝ»7üýýáïﯼ«6Çëϯ?[z^Í6ó'âòåËHKKÃÍ›7‘››‹ììläåå¡´´R©¨®®†©©)$ ÌÌÌ`nnŽ–-[ÂÎÎpuu…»»;ÜÜÜ`hhÈêåè^¶xýÙÒ³ú³mæÇqœJì²NÀqǽ:ÞÌ9ŽãôoæÇqz@ `ëÇqÿo£€"pˆ÷_¹2öúÀIEND®B`‚anytree-2.13.0/docs/static/uniquedotexporter2.png000066400000000000000000000471741500063244000220700ustar00rootroot00000000000000‰PNG  IHDRì[˜‡*úbKGDÿÿÿ ½§“ IDATxœìÝg\g÷7ðÙ,,U:¨(Ui*ÒQšX@b{ïÆúD�ÞѨÉ�£ÆÞ‰I¬± X@EE�"R•^T�Þ;ÛŸ“›?AÊÎîì.çûÂ�ìÎ^s8?fgf¯!ñù|€d"]€�ƒ !ŒJt`iii)--­¯¯ohh@¤¡¡�ÇãÉÉÉÑét*•ª¬¬¬©©©©©I£Ñˆ®‰!„¢­­-999###'''33³   ´´´¥¥¥/¯ÕÐÐÐÓÓ355533333³¶¶633#‘H®ID‚«S^ZZZÂÃÃ#""âââ’““Ùl¶¢¢"Ä&&&úúúššš:::jjjJJJ‚())Q(”¶¶¶ööv.—[___YYYYYYRRòùóçìììÌÌÌÂÂB‡£¦¦æèèèää4yòdt:@ˆAUWWß¹sçÑ£G‘‘‘,ËÊÊÊÙÙÙÑÑÑÑÑqäÈ‘Îb±ÒÒÒ^¿~]ZZª­­íãã3kÖ¬)S¦P©ðV vâ`€¸\nhhè_ý*##3uêToooooo---á­455õñãÇ!!!ñññZZZ‹-Z¹råèÑ£…·FÄ„8è7&“yûöíƒæåå9::.]ºtáÂ… C”5ß¼yó÷ßÏÏÏwvvÞµk—¯¯¯( @L@ˆƒ~`³ÙçÎ�ûù矛››—-[öÍ7ßXŸÏüøñ‘#G^½zeooäÈ‘ &X¢׉ƒ¾zøð¡……ÅîÝ»W®\ùéÓ§óçÏ›à‚�H$ŸÈÈÈ„„eeå‰'Ι3§  €Øª%qлêêêyóæÍœ9ÓÆÆ&;;ûСCDõ/¶¶¶aaaOž<ÉÉɱ²²:uê�Ç#º(D§€^<~üxåÊ•²²²¿ÿþ»——Ñåô‚Íf|Ø××wòäÉéééâŸà‚Ðh´ýû÷¿y󦢢ÂÖÖöÍ›7DW€pAˆƒîq¹Ü+V|÷Ýw¿ýöÛÕ«W•••‰®¨ÆŽommíææDt9NÝàñxË—/ ¼wïÞ´iÓˆ.g€¸\î–-[nß¾=kÖ,¢Ë@(ào ëׯ¿{÷îÇ%âJO(Ê™3g™7oÞ£G�¦L™BtEàöÄAW.\ظqãýû÷¥ãã3|>Ù²e¡¡¡‰‰‰‚O€¸�ÿ’””äìì¼{÷î] nÚÚÚœ��ù|þ›7odddˆ.>žB¡TUU•••�1BKKëÑ£G 999³gÏ&‘Hè {]€Ïç+(( × nÏž=fff¸ €8€|>ŸD"‘X,Ö€G@CüñãÇ]_¾|9‚ ÿýwÇ#íííºººt:½¼¼¼/ ðq ñëׯËÈÈðx<\F€pp8 ‚TUU©¨¨Ðh4DZ³³ëòHpp0‚ >>>�ÈÊÊzzz¶µµ………õe|ijj²X¬††ÜG€âA¤¥¥E^^^ðq:Éd2äää;?ŽÞL¹¼¼¼×/©Û ›››qB@ˆA�!C†ÔÔÔà>¬¬¬¬²²r{{{SSSçÇ+**ÑÖÖîuôK‰„WIè¹Vuuu¼€XâADCC£½½½±±÷‘Ñ9`CCC;a2™ááát:�V°×‘——g±XèÿMMM/]º4àz*++•””äää<bB ‚Œ3A�„„ÜGþå—_FŒ±mÛ¶���¦¦¦ÜÜÜ… –••�³ Ä…±±ñÞ½{ð¸¸8ìªêêêmÛ¶�1‚F£)++O™2%<<¼_ dggO˜0AAAaèСgÏž@‘üm ž`Cð�-[¶„……eggãxZÜ$%%�?>&&ÆÙÙ™èZÀ„8øGVV–¹¹yXX˜DßÍÛŠ+Þ¾}ûîÝ;¢ 7âàÿxxxðùü.Ÿ‰—………æææ§N�Z³f ѵ€qð’’’ìììnܸ1þ|¢kÁߌ3òóóSSSÿLâBüËÚµkCBBRRR:® ‘wïÞ�;wnxx¸‡‡ѵ€'qð/õõõ¶¶¶zzz/^¼d¾@±’™™éàà°téÒ3gÎ] 8ƒ]½{÷ÎÑÑqÑ¢E.\�‚+UÊËË'Nœ¨©©R€ô‘’]-€#KKË7nøûûÓh´Ó§OKtŽWTTxxx�ÉäÀÀ@Hp •à› 3f̸uëÖ¥K—Ö¬YÓñyw‰“ŸŸïææÆáp^¾|Ù1 RBtoöìÙAAAwîÜñôô¬¬¬$ºœ~{ñâ…½½½‚‚BDD„®®.Ñå ,â GÓ§O�‹‹+++;vìãÇ�‰.§¯X,ÖÞ½{§M›6yòäèèh===¢+@ˆ ÄssóÄÄD777ŸÕ«W×ÕÕ]Q/’’’lmmOœ8qêÔ©›7oÒét¢+@¸ ÄA/TUUoܸqïÞ½G��þ¼|ùr;;;•´´´ 6Hô)YúBôÉìÙ³sss׬Y³{÷îÑ£G0™L¢‹úGQQÑ7ß|cjjuóæÍÈÈHCCC¢‹@Dà:qÐ?Ÿ>}úé§Ÿ®]»¦¢¢²yóæ+VxÚ0..îܹs·oßÖÖÖþæ›o6lØ ++KT1B DYYÙ©S§.^¼ØØØèååµtéR___ƒ!šµüøñæÍ›W¯^ÍÉɱ²²Ú¾}û‚ à2p08Aˆƒ�kooôèÑ•+WÂÂÂ(Š««ëôéÓ'OžljjŠûº˜LfbbbhhhHHÈû÷ïÕÕÕ,X°téÒñãÇã¾.$„8ÀAUUÕºuëÞ½{WUUÕÐР®®îèèèàà`ee5jÔ( …Òß1[[[³³³³³³ß¾}ŸœœÌd2GŒÑÖÖ¶oß¾Õ«Wî7„8ÀŃfÏž}öìÙU«V%%%ÅÅÅÅÅÅÅÇÇ#"++kll¬§§§¥¥¥¥¥5dÈeee2™¬   ##ÓÒÒÂb±ÐÞWVV–———••úô‰ÏçÓh´Ñ£G;999:::99øúú¦¥¥%&&ÂGx@ Ä�ಲ²æÌ™óÇtyª±±1'''+++??¿¤¤ÍèÚÚÚÆÆF.—ÛÜÜÌf³Ñ(§Óé CSSSKKKGGGOOÏÔÔtôèÑ#GŽì²Ç]WWggg§©©ùòåK8� „8Hmm­½½½––ÖË—/eddD³Òììl‡Y³fýù矢Y#b ®Çår-ZÄb±‚‚‚D–à‚˜™™]¹råêÕ«çÎ�ÙJOâ`à¾ùæ›W¯^Ý»wOSSSÄ«ž1cÆ�¶mÛ&­w �àp  «W¯._¾üæÍ›DÝ�“Ïç/X°àÅ‹ #GŽ$¤!"..ÎÝÝ}ûöí?ÿü3�e´µµM˜0�ÃáÄÆÆ*((X D�ýVVVfkkkaa:€ ÀñõéÓ'[[[—{÷îÁŒW`‚câ ÚÛÛgΜÉ`0nß¾Mx‚#2|øð   ÐÐPbß@qÐ?7nÌËË{ô葲²2ѵüÃÅÅ娱cûöí $ºD n” úáèÑ£ýõ×£G�Œ��‰®å_6nÜøîÝ»+V˜™™YXX]¢ÇÄA_=þÜÛÛûСCÛ·o'º–n°Ùl//¯¢¢¢„„uuu¢Ë@D ÄAŸ|øðÁÖÖvÚ´i×®]#º–UTTØÚÚššš>yò„J…w™`P€½kjjrtt”••�ŽŽ–——'º,©©©ÎÎÎëÖ­;vìѵ °·zÁãñ-ZTSS“˜˜(æ Ž Ș1c®^½êïïonn¾jÕ*¢Ë@èàêЋ½{÷>}úôÎ�;úúúD×Ò'sæÌùöÛo7mÚOt-NX‚‚‚üüü.]º´zõj¢ké�7cÆŒ·oß&&&JÊßBô(55ÕÅÅeõêÕ'Nœ º–~ë8ŽC§Ó‰.a�Ý«©©±µµ9räÓ§O%ôJ�ÜÜ\{{{__ß«W¯] ÂÇÄA7Øl¶ŸŸŸÏ¿uë–„&8‚ &&&·oß¾yóæñãlj®a�ÝØ¼yóÛ·o>|(韚™x}ûí·/_¾¼ÿ¾®®.ѵ`æÌ™ßÿýæÍ›_½zEt- N¤®_¿¾téÒëׯ/\¸�èZÃçóçÏŸž˜˜8bÄ¢Ë` Ä£äää &lÞ¼ùСCD×B°ææf''' …£  @t9ô„ø S^^nkk;zôèÇ�ÃÐùøñ£­­­««ëÝ»w¥ï£ª@êÁ1ñÁ…ÅbÍ�;W^^þöíÛ�à(ƒ[·n=xðà×_%ºú B\šeddtydãÆ�©©©AAA***„”$ž<==�=ºgÏž���Î�·´´|üø‘ ¢èq©aaa±|ùr&“‰>râĉ?þøãæÍ›æææÄÖ&†¶nݺjÕª… vüåËÏÏ;v¬­­-‡Ã!¶60À1q©µ|ùòëׯ“H¤1cÆ<|ø033sêÔ©?ýôÓ®]»ˆ.ML±ÙlOOÏŠŠŠ7oÞ$$$øùùµµµq8œ��¢« {âÒ©­­MCC£¥¥A�¦¨¨Èáp|||nܸçî0”••ÙÚÚª¨¨dggóù|�G¥RçÌ™sëÖ-¢K {p8E:Ý¿¿µµý?›ÍnhhhnnvssƒǦ¦¦6vìØÌÌL.—Ëãñáp8ÁÁÁ D—@÷ Ä¥Ó•+WÈäÿÛ¸h$­[·níÚµp„·'¥¥¥ÎÎÎOž<éòöÍq¢ªN‘B•••ººº\.÷˧(Š››[PP�´ÞùaÀ}||êëëÙlv—§(Š‹‹Kdd$uÐ Ø—B·nÝÂ8lòòåËÄÄDQÖ#þüóϪªª/A.—URR"úªè„¸úã�?ºÝ '“É&&&111žžž¢¯JÌ�9sæÊ•+JJJ4íËg©TêÍ›7E_½‚Ã)Ò&//ÏÄĤ˃4�L&ïÞ½û»ï¾ƒÛ `(//ß±cÇÍ›7)J—“&&&999D@O`O\Ú\¹r¥ó¾$zzsÒ¤I¹¹¹€Ǧ­­}ýúõˆˆƒ.Óäææ¦§§U=�—*|>ÿÊ•+v)Šžž^hhèãÇ�‡ FlmÄÕÕ533ó·ß~“““ëø‹H£Ñ®_¿Nla| §ˆ…öööêêêêêêæææöövAêêê¡Ñh A%%%ƒ¡®®®®®ÞùÚÁ.¢££'Nœˆ¾�ÇãmܸñàÁƒ0Ãê€nذáÙ³gd2™Çãihh”••aL†×v ï ÄEŠÏçççç¿ÿ>??¿   ??ÿÓ§OåååÍÍÍ}�D"©««kii�;vÈ�!‚¬[·îÒ¥K$ÉÅÅ% ÀÔÔT˜ßÍ`q÷îÝM›6UUUñùüçÏŸOš4IØÛ€¾ƒºÚÚÚÈÈȸ¸¸¤¤¤ää䆆‰¤¯¯ohhhhhh`` ««‹îšihh())¡‡­UUUa±XèGç›››«ªª*+++++ËÊÊ Ðø¨¯¯GÄÀÀ`ܸqOŸ>¥R©§OŸ^ºt)±ßµ”ijjÚ¹sg@@€µµµŠŠŠ°·ãøñã'Nœhgg×í¥2t!.\.7&&&$$äåË—©©©$ÉÒÒÒæ,--ét:^몬¬LNNNNNNJJŠŽŽ®ªªb0&Lðôôœ5kÖÈ‘#ñZÑ Ôe;"bjjêää$ìí˜��ðùógØŽ / ÄñÄçó_½zu÷îÝ   òòòQ£FMš4ÉÃÃÃÍÍMdówçåå½|ùòåË—ááá555666~~~óæÍƒ{HölG Iøuuu/^Dçé=zôþýû333‰-‰ÃáDGGoÙ²E[[›L&Oš4éÎ�;,‹ØªÄlG q Ä•ŸŸ¿råJYYY•M›6½ÿžèŠºâp8<˜6m™LÖÕÕýí·ßZZZˆ.JìÀv B|à –-[F¥R�ŒŒÄÿ7êÇÛ·oWPPÐÔÔ<|øpkk+щ؎@¢AˆDKKËž={deeMLL®^½Êf³‰®¨*++wíÚÅ`0 ‚‚‚ˆ.‡H°�€ï·���aÆ)++Ÿ8qB²~í;+--]¼x1‰Dš2eJQQÑå¶#�âýÐÞÞ¾eˉ´hÑ¢òòr¢ËÁATTÔ¨Q£ÔÔÔîÝ»Gt-¢ÛHñ¾***3fŒ’’Ò�7ˆ®O---kÖ¬AdË–-‡èr„¶#�2â}’žž®¯¯oaaQPP@t-Bqûöm:�>kÖ,é>KÛHø°Oï¦L™2f̘àà`‘}ÖCôbcc¿úê+ssó§OŸÊËË]þ`;©!Þ‹üü|''';;»{÷îÉÊÊ]Žpeffººº:99aÌÕ'‰`;i!Ž¥®®ÎÎÎNUU5""b�Lèïéé¹bÅŠ3gÎ] n`;iFðáñ¶xñbB.`8räº�ôôôz]8%%ÅÛÛ[YY™Á`xzzÆÄIJêÀÀ@ABBBD¬HÊväóù¡¡¡ÆÆÆ EðUKßvÝ‚ïQpp0á¿ÖÖÖ½þòÇÇÇÓéôyóæ•––VUU­Y³†J¥†…… ²Þ%K–èêêÖÖÖ 2ˆ˜�”혟Ÿïëëkee¥¤¤„Kˆó¥k;‚ž@ˆw�Ãᘘ˜,Y²„Ø2zýåçr¹æææ:::W#p8SSÓ¡C‡¶··x½µµµ{÷îðbBR¶#ŸÏ_°`Á/¿üÂf³õôôð q©ÙŽÜ ª{÷îÝ+((Ø»w/Ñ…ô"***##ÃÏϯcbk …²`Á‚âââ���«ªªºeË–Ó§O744àT)1$e;"rùòåÝ»wS©TÇ”ší0@ˆwïܹs³fÍ211!º�^¼|ùA�ñãÇw~ý2<<\�‘7mÚÄãñnܸ!È „“”íˆ Ž÷—èL:¶#À!Þ�ÆÆÆ×¯_ûûû 2“ÉÜ·oŸ™™™¼¼¼ššš¯¯ïǹ\.‚ ?ýô‰DB.üôéSôuuõ/‡ÊÎÎöññQVV–——www���íü‚ úúú�—×ÓÓC$77W�úUTT&MšôäÉA!–mGá‘‚ízAôñq„^][SS#È «W¯VVV~öìYkkkyyùŽ;‰ˆˆèX@AAÁÙÙ¹óKlll† Òùkkkeeeww÷˜˜˜¦¦¦ÄÄD+++™ÈÈHt///Aâãã;¿*//A�qãÆ R?ŸÏ¿té’‚‚“Ép¢HÐvì Çcâ(Iߎì‰w#77wذajjj‚ nnnîååE§Óµ´´Ž92°7õ tvvf0ãÇ�¿~ý:‹ÅÚºu+ÆKø|>‚ $i€¥ÿ�µµuKKKii©€ãEÒ·#^$};lâݨ¨¨ÐÒÒp�©S§¾~ýzíÚµñññè»ïœœ77·þŽ#''gooßñ¥¥¥¥®®nZZZYY‚ èÇÇÑ;©w@¿ü“åÚÚÚ‚”—— 8Q$h; •¤oG€ B¼ÍÍÍ CÀAΞ={õêÕÂÂBOOO%%¥©S§¢,÷×�!CºìSkjj"RYY‰ ˆ™™‚ Ÿ?î¼@II ‚ ‚ŸÍSTTD¤©©IÀqˆ"AÛQ¨$};lâÝÐÐШ®®p‰´dÉ’/^Ô××ß¿ŸÏçÏž=ûرc �Éd‹Õù%õõõ_ŽóåÅaè¯=îîî‚$%%u^ýÒÓÓSÀo¡óŠ$‘mG¡’ôí°AˆwCKKKðˆ***è¥#4ÍËËëþýû$)44´ct—U^^^TTôå8ÍÍÍiii_¾{÷®´´ÔÚÚZGGAWW×Ñ£G¶··£ p¹Ü[·n :ÔÇÇGÀom‚àG$ˆ"AÛQ¨$};lâÝ?~|eeeNNŽ€ã¬_¿>==�ÉdVVV>|˜Ïç{xxt<;yòäÒÒÒ3gÎ477lݺµÛ}%…M›6½y󦥥åíÛ·‹/–‘‘9yò$ú,™L¾|ùrmmíŠ+ÊËËkjj6nܘ—— '''`ýQQQúúúèUI$AÛQ¨$};‚^zmŒ˜âp8ªªªÇ�d�ÔÔÔuëÖ�5 ½¾ØÁÁ! €Çãu,P__¿zõj:�îââ’˜˜hccƒn”]»vuž8)!!ÁÝÝ�Á`ÐétWW×/ç·JNNž6mš’’ƒÁðððp¬kÖ¬Áe(BHÖv|ôèÑ—¿ž‚�’ôí°ÁT´Ý[¾|yrrrZZšàêI¨wïÞY[[‡„„x{{]ËÀÁv”Ží0@ˆw/33ÓÒÒòÁƒÓ§O'ºb,\¸0--íÝ»wd²sƒí(Û`€ïÑŒ3>|ø�˜˜(õ7‚ùRBB‚““Óõë×çÏŸOt-‚‚í(ÛôB¼G?~´²²Ú´iÓÁƒ‰®E¤˜L¦���¶¶öóçÏ¥à(lGéØŽ '”]ƒ˜RQQQSSÛ»w¯ƒƒƒ¡¡!ÑåˆÎ×_ýúõë°°0鸛0lGéØŽ GÄžWs<oÉ’%ŠŠŠÉÉÉD×""?þø#…BA?Õ"5`;)!Þ ‹åå奭­�––Ft-BwìØ1‰tþüy¢ ÁlG ­ Ä{רØèî¬Nt-ÂÂãñvîÜI"‘Ž=Jt-ÂÛH%ñ>iooŸ?¾ŒŒÌéÓ§;ÐC:ÔÖÖΞ=[FFæúõëD×"\°�ô�ï+.—ûÃ?P(__ߪª*¢ËÁMTTÔ°aÃtuu»½A�ô�í¤ „xÿDGG6LSSóÏ?ÿ”ô]¹ÚÚÚ�7J_œõlG 5 Äû­®®ý�qttLHH ºœ�`³Ù¿ÿþ»†††–––¤ØÀÀvÒB|€RRRÐÛãz{{ÇÅÅ]N_±X¬Ë—/R©Ô-[¶ÔÕÕ]Á`;I!.�Ç�;::"âééÈb±ˆ®¨G‡200 Ñh«V­*(( º"1ÛH.q<{öÌÛÛ›L&ëèèìÝ»7##ƒèŠþ‹Å [°`�¬¬¬ŠŠÊÖ­[?~üHtQbJ"¶#�F“““Û¼y3lG€‚ÇÍÇ�÷ìÙ£««‹ ÈèÑ£÷íÛ—’’BÔaÊ–––���+V ÷zwttüã�?ZZZ)F²ˆùv\¶l"èéC IDAT�N?~|ff&!%q`áŒÇãÅÆÆÞ¹s§¼¼\CCÃÃÃÃÓÓsâÄ‰ÆÆÆB�´­­-%%ååË—áááqqq,ËÎÎÎÏÏÏÏÏÏÀÀ@xë•JÛ1((èóçÏbµ³²²–-[öîÝ»ìܹæ˜ä Ä…"''ÇÓÓsĈ3fÌ�ŽŽniiQRR;vì¸q㬬¬ŒŒŒŒŒŒ¹c›ÍþôéS~~~NNNJJJrrrVV‡ÃÑ××÷ütw‚Ç㥦¦¢‘*>Û‘ÃáüöÛoûöí³µµ½råÊ šØ t!Ž¿¬¬¬I“&ééé=}ú}Ìb±ÒÓÓ“’’’“““’’222Ðû3Œ#Fhjjjii©««khhÈËË3 A”””(J[[[{{;ŸÏ¯¯¯oii©¬¬¬¨¨¨®®.---**âp8‚hhhŒ3fܸq666666#GŽ$öÛ—b�·ãÇ«««ÑM0àíX\\üùóg.—‹ h;¦§§/]ºôÇGŽY»v­°¿} ž Äq–šš:yòd33³ÐÐPEEÅn—áóùŸ?.((ÈÏÏÿôéSyyyUUUuuuuuu[[[SS‚ ���\.WNNŽN§“Édeeeuuummmuuu---###CCC###˜hTô233ÇŒsòäÉéÓ§ ²ãââÊÊÊ‚ƒƒ���¶ÛÛÛ8pôèÑÉ“'ÿþûïðÞk0"ðx¼ôIJJ2dˆ««kSS“€Cݾ}¶ŽØòðð7n‡Ãpœ÷ïß“H¤°°0lj��522ÒÐиwC‰§Dpóöí[///[[Û'Ož o¥�Túûï¿###Ïž=K¡PÊÜÜÜÕÕõôéÓŽãä䔜œýï¿ÿ¦ÑhD—„(33óäÉ“ÔÐÐÀkL …²nݺßÿ½±±—UTT®_¿~ûöíÀÀ@kkëèèh\†b B\ ¡¡¡³fÍš5kÖµkרT*ÑåáÚ¼y³¥¥åš5kðvíÚµ<ïúõë8Žéï‘1zôhww÷Ý»w³X,bB|àgÍšµdÉHðÁÇó™]¨ªª.X°àäÉ“|\¯÷ÕÖÖ~øðá¹sçΞ=kcc“ššŠãà@|@ˆÐíÛ·,X°jÕª‹/Â瞥îç3»Øºuk^^ÞóçÏñ–D"­]»6==]MMÍÎÎîÀ�è‹€T!úG‰tãÆ *•ºcÇá­®+;vìPUU­¬¬Þ*ÜÜܦOŸ.¤Á¹\î‰'deesss…´@Ø…ì·€€€%K–lß¾ýÈ‘#D×DAç3¿„×µ†Ý"“É[·n}ûöm{{;úYS>|T[jýWDœ?žL&ïÚµKØ+‚=qñ�×ç3±q8œ#Fü¿ÿ÷ÿ„º‹µÿ~ …2yòäÏŸ? u]@4 &úáèÑ£‚ü÷¿ÿÁº ÄÅÄßÿM&“Esç¶C‡)**644{Eqqq&&&***W¯^öº€°ALôÕ¡C‡H$ÒñãÇE³:qqÐØØ¨§§·zõjѬ®¶¶VAAáìÙ³"XWkkë–-[H$’¿¿uuµÖ„b¢OÐ?uê”ÈÖ!.Dp>³‹Õ«W›˜˜ˆìFBaaaúúúZZZ>Íî &z÷ý÷ß“ÉäË—/‹r¥â„ËÈÈ ÑhçÏŸåJß½{‡Ë¼†}W__�ÎE¾dÉÁgߢ1�…ÇãmÛ¶�B¡üõ×_"^5„8áDs>óKB½Ö°'wîÜQWW700ˆŒŒñª�€àÃñùü­[·ž>}ú¯¿þZ¶lÑ呺uë–�>ŸÙ+¡^kØÿ÷ïß[YY¹»»oݺ•ÉdŠrí@ DÿS\.wåÊ•222DͲ{âñùÌ.Ds­aO®\¹¢¨¨hnnž””DH ¿`O¼h‚߸qãîÝ»³gÏ&º jè|³$dí¸ÏkØ/K—.MOO×Ðа··ß½{7›Í}  ˆþ+"v8ÎâÅ‹åååŸ={F`°'NBÎgv!Êk »Åãñ.^¼(//oooŸ��MT /`Oü_X,Öܹsƒƒƒ>|èååEt9€Bšo¶_„4¯aß¡3g%&&r8œ±cÇþúë¯<��J@ïˆþ+"F˜LæŒ3 FDDѵÀž81DùùLl赆ľäóùl6ûСC4mÒ¤IEEEĺ{âÿhmmõõõ}õêÕóçÏÝÜ܈. ©©iÇŽ+W®Ò|³ýbaa�Ë=”D¥RwíÚS\\liiyéÒ%bë_‚GiiiùꫯŸ={&¿À€ÄžÏüÒæÍ›CBBD|­a·ìììRRRÖ¯_¿aÃ??¿êêj¢+ÿBinnž>}ú»wï"##mmm‰.C4óÍö ¾÷P�N?tèPXXXBB‚¹¹ùýû÷‰®üc°‡x}}½——WVVVxx¸••ÑåˆÃùÌ.ˆ½Ö°[“&MzÿþýÌ™3gÍš5wîܺº:¢+ƒ;Äëêê¦L™RRRmaaAt9€0~>›0î¡, %%¥‹/†††ÆÄÄŒ;6""‚芻Áâ•••nnnÆÆÆD—#Vç3» üZÞx{{§¦¦Ž;ÖÓÓsݺu­­­DW4x Ò¯¨¨ðôôlllŒˆˆ044$º@$q;ŸÙzå/^]HWšššÁÁÁ·oß¾{÷îøñãß¾}KtEƒÔ` ñâââ &°Ù옘˜#F] ’žÏìBL®5쉿¿jjªŽŽŽ££ãîÝ»Y,Ñ :ƒ.Ä‹ŠŠÜÝÝ©TjDD„žžÑ傉áùÌ/‰Ïµ†Ý6lØ‹/Ξ={æÌ[[Ûôôt¢+\WˆüøÑÍÍMQQ1**JGG‡èrÁÄö|fbu­a·Ð�é§¥¥)**ÚÛÛÃÇôEi…xNNŽ‹‹‹ššÚ‹/ÔÕÕ‰.LœÏgv�^kxùòeñ¹Ö°[†††‘‘‘Ø·oßĉ ˆ®hP ‰ÛYo!ÉÊÊš4i’žž^XX˜ªª*ÑåtÏ××÷ãÇ�èÿ›ššÊÊÊLLL:ž]»víæÍ›‰©LíܹóòåË999b{4¼³ºº:}}ý#GŽ|ýõׂdggŸ={ÖÅÅeÞ¼yD—Ö�ôôô¥K—~øðáÈ‘#è½ß€=y‹(¤¤¤hhhL˜0¡±±‘èZ°˜››cl©Ÿþ™è%Ø�?þ8lذ _ŠÃ|³ý…ÞCùáÇ$ A¢‹êQ[[Û®]»(Ê´iÓJJJˆ.GšIˆ'%% 2ÄÕÕUüoû믿R©ÔžB}:…BÙµk“É$º‰'ñ!ž‘‘Ñùl•J•““›7o—Ë%º´ºzõjç÷þ4­®®Žè¢$‹Åê6þÈd²¿¿¿Dœñæóù%%%úúú_~#zÍ�Ç»xñ"ƒÁ°°°HIIéòl{{ûÒ¥Kß¾}KHmGâC¼c7¼sŽkii‰ó�Nl���²²²ßËŒ3ˆ®H²}9»‰D"‘Hû÷ïçñxDW×ùùùêêê]Ž�¿yó†èº®°°pâĉ4mÿþý�� íܹA�aÆ544Xž¤�ìï²Þ9Çutt>|ø@t�4gÎô/‰Dº{÷.ÑåH¶óçÏw> …"''Dt]‘––Æ`0:¿Q{úô)ÑE „Ëåž8qBVVÖÑÑ177—ÏçGGG£¿Ô4mîܹD($;Äýýý»}§Œæø°aÃjkk‰®q ‚‚‚ПcyyyIy¿/¶V®\ÙñCB£Ñ455%ú}zDD�FCE&“y<‹Å’Ð{z{{+(( 2gÎ:�Nt9’-66ý!¡R©ÖÖÖiii666D5pnnn���hˆS©T ý ïÂÚÚ:11qãÆ�üñGii)—ËEçóù7nÌÊÊ"¶<1×ㇼqÇår+*****êëë¹\nSS‡Ã‘———••¥Óé***:::jjj}ðÀ�T*µKˆS(.—;lذmÛ¶­[·NNNïïCXºôÇÎÎîåË—ÆÆÆ/^¼X¤ÌÀ~~Z[[óóó!‘HóçÏÿý÷ß;Î7H®¯¾úêÂ… ëÖ­ãp8!Žûï—ˆÉÊÊzxx>|˜ÿï)ù8άY³RRRÜ›‘ôþ`Ö,†mmm‰‰‰éééïß¿ÏÈÈ(((¨¬¬ìøÛ999}}}SSS ssóqãÆ�=ºÛ£ÞYYYæææ�‹§Ñhl6ÛÞÞ~ûöí³gÏó¢…ÝI‡Wêêê&L˜@"‘:ôí·ßЦxhkkÛºuk@@€•••’’’üüÔ××›™™UUU}99•J]³f͹sçú>Ú úýÂ3Äy<Þ›7o?~™˜˜Èd2ÕÔÔÐv˜ššêèèèêêjii©©©‘ÉdEEE*•ÚÚÚÊd2ÛÛÛkkkKKKËÊÊŠ‹‹333322²²²X,:û »»ûW_}5lذŽuÍ›7/88Ý §ÑhgêÔ©{öìqvvÆëÛÁ�(û#‰„Ñ%%¥ÖÖÖU«V}÷ÝwÒ×yyù#FLœ8Q ~~æÎ�{ÿþýn�Ž¢nݺ…=ïîàýýÂåÈzLL̆ tuu144\µjÕÕ«W?}ú$Șl6;11ñ·ß~ûꫯ”••ÑI ~ùå—’’’ÌÌÌŽó×T*uÙ²e¸|#B"âþàU¶È¯?G�õöö†þ|I¬~~’““Áx÷ŒÆnOß²Ô÷›@!ÞÔÔtöìYKKKA¬¬¬~øáôSï¸c2™Ož‚ t:}ûöíÅÅÅÂX#.ˆêϬY³ž={&Œá úƒmðô‡Çãݽ{wÕªUhS©Ô/纡Ñhööö�?†=xúƒm€!ÞÔÔtâÄ mmm999ÿçÏŸã[VO˜Læ�;wFŽI"‘ÌÍÍïܹ#žŸ× ¶?“&M"‘HVVVП. ?ØÄ¡?/^œ={6ƒÁ@³»#Ç)Êž={øƒ»?_êwˆs¹ÜsçÎ 2DIIiïÞ½DMÝ�””ôÕW_‘H$;;»„„Bjèôôô§›ÍŽŠŠúþûïmllÈd2é¶nÝ ýé¬!ž””dkkK£ÑvìØ!3ï$''»¹¹‘Éäõë׋Ã#ÐlÐlПžÔÕÕÝ»wÏÏÏOFF†L&C:ëkˆóx¼_ý•F£Mœ8ñýû÷B­©_x<Þµk×´µµ‡K`Ðì2 ?Øe@°Ë€þô¤O!^[[;yòd�vøðaq8ô¥ªª**•Š~X@Ä ?Ø ?Ø ?Ø ?Øzñ¢¢"ssó¡C‡~è�Çûí·ß(ʆ D9S>ôôôô§W½„x^^ž¾¾¾¥¥åçÏŸES�€‚ƒƒétºŸŸŸhîýÁýÁýÁýé ¬/--9r¤���8œó黨¨(yyù•+W û�ôôôô§�z ñ––kkkt6Ñ”‚£ÐÐPt¦yá­úƒ úƒ úƒ úÓw=†øúõëUUU?~ü(š:pwáÂ2™!¤ñ¡?Ø ?Ø ?Ø ?}×}ˆ?|ø�D"Š áñóóÓ×ׯ{1è6è6è6èO¿tâL&ÓÐÐpÑ¢EÂ^7*%%�¢ˆÁ`xzzÆÄÄà5rmm­††ÆÎ�;ñýÁ&âþðùüÐÐPccc …‚ï°ÐlÐlBêÏ—º ñãÇ�Óéô¢¢"a¯›ÏçÇÇÇÓéôyóæ•––VUU­Y³†J¥†……á5þ©S§äääð}SýÁ&Êþäççûúú¢sjãþKȇþôúƒMýùR×Go‹óÍ7ßu­ë277×ÑÑé¸�$‡Ã155:th{{;.«`2™Ã† Ãñ�!ô›(ûÃçó,XðË/¿°Ùl===aüB°A°áÞŸnu ñ°°0A²²²„ºVTDD‚ ›7oîüà�ÁñpØþýûµ´´X,.£A°‰²?|>¿ãÏ›�~ ùПÞ@°áÛŸnu�´÷Ö­[ŽŽŽfffý¾»Dÿ½|ùA�ñãÇw~ý2<<¯µ¬X±¢²² DÁA°‰²?‚ˆà.ÒÐlÐløö§[]C<::ÚËË ¯Ñ™Læ¾}ûÌÌÌäååÕÔÔ|}}>|Øq§»ììlAÐ;öìÙÇŽCŸuwwG$))©óKÐ/===ñªA�šš555ÁÇ�þ`qDúƒ úƒ ¯þôä_!޾‘©ªªÂktô �æååuÿþ}‰Š>ëêê:zôèÀÀÀöövô.—{ëÖ­¡C‡úøøàU‚ UUU¸¼Gƒþ`qDúƒ úƒ ¯þôä_!nll,''—’’‚ã Ö¯_ŸžžÎd2+++Ñ{yxxü³n2ùòå˵µµ+V¬(//¯©©Ù¸qc^^^@@€œœŽ5$''[ZZ >ô›ˆû#2ÐlÐlxõ§G]>üãèè¸aü>J”ššºnݺQ£F¡×i:88t™+=99yÚ´iJJJ ÃÃÃÇ žP<OMMíäÉ“¸ŒýÁ&âþ‹Åš?¾¬¬ìßÿ-š‚Ád2.\(###‚“ (è6è6è6èO_ôâ|>ŸËå~óÍ7$i×®]B½Ý§€>~üèä䤤¤ôüùsQ®úƒ úƒ úƒ úÓ«ÞCuùòe‡üü|¡40wïÞUUU577ÿþ=!@°A°A°A0ô5Äù|~ff¦µµ5�NÿñÇ…}ùzßNŸ>A�µk×¶¶¶X ôôôô§'ýq>ŸÏb±>Ì`0ŒŒŒ®]»Fì<5•••»ví¢Óé£G�Žˆˆ °’ÐlÐlÐlПnõ/ÄQÅÅÅË–-£R©¦¦¦þù§è?XUTT´sçNƒ¡©©yüøqq;RýÁýÁýÁýéb !ŽÊËË[¾|¹ŒŒŒššÚ¶mÛDp0ˆÅb=zôÈ××—B¡hkk=z´¥¥EØ+0è6è6è6èO‡�‡8ª¼¼üàÁƒ#FŒ@ÄÌÌlÏž=‰‰‰ø¾Íill|ðàÁ²eËÔÔÔH$’§§ç�;w˜L&Ž«è6è6è6èŸÏ'ñùüߨ�Ç‹�� þô铲²²‹‹‹‹‹Ë¸qã,,,tuuû5‡ÃÉËË{ÿþ}|||tttJJ �Çsttœ={öìÙ³ /XÄ ?Ø ?Ø ?Øyð ñÎÞ¿ÿêÕ«¨¨¨˜˜˜ÒÒRAÔÔÔLLL´µµ‡ª©©©¬¬,+++///++ÛÔÔÄápššš‹‹‹+**ŠŠŠrssY,•J5j”««ëĉ'Nœ¨¥¥…o�D�þ`ƒþ`ƒþ`„ýÁ?Ä;«©©y÷î]FFF~~~yyyIIIEEEcc#“Éliia±X ƒF£)***))éééikkëëë›™™™››�=ZVVVxµ‰è6è6è6Œþ477³Ùléè�pCÄÐ�;wæÍ›'éG&º! B$„8H0q�`â Á Ä@‚Aˆ€ƒ ! B$„8H0q�`â Á Ä@‚Aˆ€ƒ ! B$„8H0q�`â Á Ä@‚Aˆ€ƒ ! B$„8H0q�`â Á Ä@‚Aˆ€£]ˆ‚¯¯ïÇ�Ñÿ755ÉÈÈXZZv<»víÚÍ›7S™` ă‡222:?òþýûŽÿ755‰¼"|ÀáÀ °téR*µÇÝÖ¹s犲‘ø|>Ñ5€Ð>üËÄ#‘H666‰‰‰„T%8Ø C‡µ··'“»†…BYºt)!%áB0X,Y²„D"uy�ÇãùûûR. ăŗ¾)Š›››¶¶6!õàB0X¨««{zzR(”Î.Y²„¨zp!D/^ÜùÜ&™Lž9s&�õB0ˆÌœ9“F£¡ÿ§R©ÞÞÞ***Ä–$ qÀ ¢¨¨8}út4ǹ\îâÅ‹‰®HPâ€ÁeÑ¢EA:�îããCt9‚‚ .ÞÞÞ ‚Ì™3‡N§]Ž `î€tâñx uuu ,«cv”ÖÖV[[ÛˆˆˆaÆ=yò=´B&“•••åä䔕•••• ­½àc÷IÕØØ˜——÷ùóçâââÊÊÊŽÑàdN+4ÓUUUµ´´´´´ôõõѵµµ :ÎŽB >}ú”––öþýû¼ÿ©¬¬DŸÒÐÐèµjjjèµŠŠŠŠŠŠ’’�Fë¸ ENN®ã(JCC�ÇC„Íf777·µµ5tRWWWQQQ^^^RRRQQñùóçÖÖVA¨TêðáÃ����ŒŒÌÌÌ,--­­­•••‰è „8@\}ú UQIDATô):::)))---55µ®®ŽD"�1ÂØØ P##£áÇËÈȈ¦¤†††‚‚‚¼¼¼üüüüüüÜÜÜìììÚÚZAFŽimm=fÌ{{{'''‘��ˆ‘ìì숈ˆØØØ¨¨¨ââb++«±cÇZ[[[[[[YY)))]cWÅÅÅ蟙´´´äääÂÂB …2fÌ— &¸»»«©© oí₵µµÅÆÆ¾xñâÁƒÙÙÙ hNš4ÉÙÙYâ. ©¨¨HHHˆ���‰‰IHHàñxcÆŒ™>}º¯¯ï¸q㾜�K@âb´··‡††Þ¸qãéÓ§ííícÆŒ™6mš���½½}—éM$WCCÃóçÏŸ}útLLŒ¥¥åÎ�;.\8€ãHâ!jhh8vìØñãÇååå·nݺfÍuuu¢‹/)))'Nœ¸y󦉉ÉÿûßY³fõk¯B <ïÂ… ßÿ=ŸÏß¹sç–-[ÐK@·rss÷ïßçÎ�qãÆ�?~üøñ}|!L€À_ffæÄ‰·mÛ¶zõêÂÂÂÿüç?�àØLLLþþûïäädEEE‡íÛ·£Ÿí„8gÇŽ7n‹ÅJLLüõ×_%ý® ¢dmm~ñâÅ?ÿüÓÂÂâíÛ·½¾B€›¶¶¶Å‹ûí·?üðC\\œµµ5ÑI‰´jÕªÌÌL“ &\¿~{y˜Š€�ššš©S§>yòÄËË‹èr$›¶¶vhhèîÝ»—.]š‘‘ñË/¿ô´$ì‰pÐØØ8uêÔššš7oÞ˜àG�%‘H$I__¿/Ë?~üØÄÄ„JÅÚ�MLL\¾|ùˆ#ètºššš……Åœ9sΟ?_PP€SÕÝ£P(GŽù믿Ž9ràÀ�—ã€`ÚÚÚ&Nœ¨««›ŸŸOt-|>Ÿomm­§§‡½L~~¾¯¯/:£…Bév.—»cÇ*•ºsçά¬¬öööòòògÏžMš4 ÍO6›-„ò»º|ù2‰D:qâD·ÏBˆõŸÿüGIIéýû÷Dò�¾„ø‚ ~ùå6›­§§×Sˆ÷Ýw‚\ºt©ËãgÚ´i" q>Ÿøða*•š””ôåSp�8@ ©©©vvvgΜY»v-ѵüc̘1ÕÕÕŸ?ÆX¦­­ �Q__¿¼¼½urgÙÙÙæææcÇŽíö‘¸¸8'''6›�}(/|>Ê”)‰‰‰]&O‡câ�lÛ¶ÍÅÅeÍš5DÒ?½Îp{éÒ%�çïïßí³ŽŽŽ|>_4 Ž ‰DºpáB~~þüÑå)qÀÀåææFEEýç?ÿÁkšl&“¹oß>333yyy555__߇r¹\A~úé'ô¤¥‹‹ ºðÓ§OÑGºý(vv¶���²²²¼¼¼»»{lll¿*‰ŠŠBÄÊʪ/ s8œÛ·o{yyikkÓétKKË“'O¢7~ÃËÈ‘#çÏŸáÂ…®Oˆæ€@*íÚµkĈ\.¯W¯^­¬¬üìÙ³ÖÖÖòòò;v ѱ€‚‚‚³³sç—ØØØ 2¤ó#è/ÝÝÝcbbššš­¬¬ddd"##¿\cOÇÄuttyóæM_Ê~ôè‚ ¬­­­ªª:uê™LÞ±cG_^Ûw¯_¿F$%%¥óƒâ€�sppØ´iŽŽ1ÂÉÉ©ó#&&&qAâââ:IOOGÄÚÚúË5b‡xBBB_Ê~ôè‘››[çG/^L£Ñúòò¾ÓÐÐèr™ N ܇LMMqpêÔ©¯_¿^»vm||111),,ìü„8`àÚÛÛñ½æÙ³g¯^½ZXXèé驤¤4uêÔàààŒ3dÈ�.‡é555©¬¬ìã®®®‚ »ð½jhhØ·oŸ¥¥¥ªª*z˜~çÎ�‚ôq«¾“——okkëü„8`àTTTjjjp�D"-Y²äÅ‹õõõ÷ïßçóù³gÏ>vìXÇd2™Åbu~I}}ý—ã444ty�o4ÊûbݺuT*500°Ûg¿ýö[2™œ���~éëëûßÿþwÍš5¹¹¹<�Ïç?~A>Þ×p×ÔÔ¨ªªv~B0pæææÉÉÉ8¨¢¢‚&#�Fóòòºÿ>‰D íX@GG§¤¤¤ãËòòò¢¢¢/ÇinnNKKëøòÝ»w¥¥¥ÖÖÖè‘î¾011Ù¿ÿÛ·o¿¼ª/''çâÅ‹sçÎ533C„Ë寯ÆjkkoÙ²ECC}Ðemmm™™™�„ z `·û¶~ýúôôt&“YYYyøða>ŸïááÑñìäÉ“KKKÏœ9ÓÜÜ\PP°uëÖnw®6mÚôæÍ›–––·oß.^¼XFFæäÉ“ýªdïÞ½»wï^¿~ýîÝ»sssY,VIIÉåË—ÝÝÝ­¬¬._¾Œ.F¡PÜÜÜÊËË�9R]]ÝÖÖÑ͵€»wï�Ç›2eÊ¿Å÷Ì)`PihhPPP8wî^¦¦¦®[·nÔ¨Qèuâè T}}ýêÕ«uttètº‹‹Kbb¢�� šf»ví:räú==½„„wwwƒA§Ó]]]cbb:¯½(°‹€€€/KJHHX²dÉСCi4zdž“'O2™ÌÎËTUU­[·]FKKkùòå»wïFÇ´±±Á«9®®®~~~]„�ݲqãÆààà÷ïß«©©]‹4 ž3gNddäĉ;?!HCCƒ¥¥¥»»û•+Wˆ®EjÕÔÔXXXøúú^ºt©ËSpL eeåóçÏ_»víìÙ³D×"�˜Læ¼yóh4ZÇÁ¢Î(Xs�@˜˜˜¨¨¨lݺU__ܸqD—#U¸\î¢E‹¢££Ÿ>}j``ðåp{6¶nÝZQQ±~ýz&“ùõ×_]Ž”hjjB¯šþüùرc»]B€�ƒÒéôÍ›7§¤¤œ9sFVV–èŠ$[^^ÞÌ™3kkkÃÂÂ{Z މpóý÷ßß¹sg„ ïÞ½#ºIÅçóÿúë/;;;…ÄÄDgggŒ…!Äxúꫯ޼yC¡PlllöíÛÇd2‰®HÂNžÌËË322Z¸páÂ… qù~!Ä„ikk «­­544ôööž4i’³³óÀŽ-ˆ•œœœèèè°°°gÏž566š™™ùøøÌ�;×ÎÎǵ@ˆˆÇårãããCCC?~ŒÞÖrÔ¨Q&Lpqq±··744$“%àRº¶¶¶ôôôׯ_GGGÇÆÆVVVÊËËOœ8ÑÇÇÇÛÛ{äÈ‘ÂX)„8@¼ÔÖÖÆÆÆÆÄÄÄÄļ}û–Åb)**ZYY�3f̘1ÖÖÖÆÆÆý½„Cø|~IIIvvvêÿäääp8uuuggç &8;;ÛØØr¼»/ Ä⫽½===½#%ÓÓÓ[ZZQWW722266666>|¸®®®ŽŽŽ®®n—›ã‚ËåVTT”–––•••””æÿz#M]]Ý1�¡·Ù q€Äàñx�34///??¿¨¨¨½½]@NNNWWwÈ�!ÊÊʪªÿ¿�»Wa†(œA½Vg¡úþo%:„Älþ@‡[¥‹s œoÈ…8.y‰ˆˆTU•¦ižçz'W’$eYêÛ¼÷ú`á8Žº®þ✛çyY–óÆ=kí4M÷°l­Õ;ç4Çû¾k¦�1Û¶é8oŒ¹'wM|Qò£®ë®ëtÆ×5˲¿ò€ˆEðÃð„ˆ@Ĉ8DŒˆ@Ä>ž­¾¨wëIEND®B`‚anytree-2.13.0/docs/static/weight.png000066400000000000000000000306531500063244000174610ustar00rootroot00000000000000‰PNG  IHDR%Hè‘_bKGDÿÿÿ ½§“ IDATxœíÝy\×ú?ð3YHÂ&›„Õ‚lJq¢‚Š J+*hUDA_¸´Vk{ÕVkk­èE½ÕÚkµnT+EDÜÙDPÜ!RY%ìû²üþ˜ûÍ�"Ff2I8ï¿dLæ<<ða&“É9ˆD"ɉDt¤–`r H09¤ Ñ@ÿ#‰jjjjjjš››E"Q[[›P(ÔÖÖ¦Ñh ÃÀÀÀÜÜÜÈȈè2¡ÿ�É!�ÇËÍÍ}ñâE~~>‡Ã)))©­­‰D²ŸE§Ó­¬¬X,–£££ƒƒÃG}doo� ˆrj†zBàµ5¥‹Å<¸víZZZZnn.ŸÏ722B3Àb±ÌÍÍ-,,˜L¦‘‘‰DÒÓÓ£P(���|>¿«««±±±²²²ªªª¼¼üåË—§  @  :tÚ´iÓ§O÷÷÷6lÑßâ “£ YYYÑÑщ‰‰•••£G�f³Ù^^^^^^ù] …Ïž=ËÈÈHOOOOOommuuuýä“OV¬Xaaa�añPß$nÚÚÚŽ;æääpvvÞ³gÏ‹/ðˆÏ秤¤„……™˜˜P(”€€€›7oâ1$“ƒ‹¶¶¶Ã‡›™™ÑéôÀÀÀ[·n)g\>Ÿçíí� ˆ³³s\\œX,VÎЃ LÆD"ÑñãÇ���õõõwîÜÙÐÐ@H�?ö÷÷GdÒ¤I>$¤Í“ƒ¥Ç�Oœ8‘J¥nݺ•¨ÌôôäÉ6›M"‘Ö­[×ÔÔDt9&b±ø§Ÿ~¢R©žžžùùùD—óÿ‰ÅâóçÏ›™™ ><++‹èr4LgÍšE¥R8 š¯+êêêæÍ›G¡P8@t-&g Þ¾}ëàà`mm­â/'Äbñ¡C‡Èdòúõë…B!Ñå¨=xÁ€OŸ>ÝÐÐ0;;ÛÒÒ’èrdAdË–-£F� ®««»xñ"…úŠƒï„*®ªªÊÃÃÃÄÄäÆ�D—Ó_™™™sæÌYºtéo¿ýïÜQ¼WZA���¾¾¾ZZZÉÉÉjÀ´iÓâããÏŸ?¿gÏ¢kQgDŸ.ª«uëÖ–••]ˆ‚Nœ8A"‘RSS‰.D]Á³5E$%%-X° >>þ“O>!ºÅæäääåå©×1SEÀäÈM ØÛÛO™2åÂ… D×2 MMM,kÕªU ºõ_çÈíøñã•••?þø#Ñ… ”¡¡á®]»þóŸÿ¼yó†èZÔLŽ|ÄbqTTÔúõë­­­‰®ááᦦ¦ÇŽ#ºõ“#ŸÛ·o¿}ûvíÚµD‚ --­���sçÎuww]‹š�É‘OLLŒ›››��Ñ…`&$$¤¶¶655•èBÔ LŽ|233}||pÚ¹P(Œ��õññ133c0NNNGŽ‹Å8 ‡>|ø˜1cîÝ»‡ë(š&Gõõõ%%%nnn8íÿúõëK—.�1cFAAAyyyXXØ–-[¶oߎÓpRîîîÙÙÙx�¢a`räðæÍ‰DÂb±ð‚Ífýõ׆††&&&7n >räHkk+~#lmmËÊÊpBóÀäÈ¡¾¾`llŒÓþçÏŸßëõƸq㺻»9N#¢Œ��pBóÀ»eåÀãñ §ý·´´:t(!!�Ëå677K·wvvâ4"JWW·££×!4<æÈÁÐÐÐÔÔ„ÓþýüüöîÝ»víÚÂÂBôrQQQ¼ïóhhh€³‡Ê &GèyZ]];‰DYYYfff›6m:t(zÿ?z”Ã[]]~§ š &G666t:ýéÓ§xìœL&³ÙìêêêÈÈÈúúz�—ššzâÄ <ÆêåÉ“'è¤pPÿÁäÈ�F£¹¸¸Ü¿§ýÇÆÆ†‡‡ÿç?ÿ±°°9rä¹s炃ƒ>>>&LÀiP‰D’““ƒß¥vMï•–Ï®]»Îœ9SVVF&“‰®ééél6;//ÏÑÑ‘èZÔ <æÈ'$$¤¢¢âÖ­[D‚™S§NMœ8ÆF^09ò5j”§§'zÉKp¹ÜK—.­Y³†èBÔ<[“[FF†——×�7fÍšEt-’––ö÷ßÓh4¢kQ309Šð÷÷ýúõ£G�èt:ѵ(.''ÇÃÃCz’ LŽ"ÊËËÇ�·|ùò£G�]‹‚ÚÛÛ?úè£Q£F¥¤¤À¹£_ç(ÂÚÚúøñã?ÿüsll,ѵ(B,¯\¹²­­íìÙ³06Š�÷­)héÒ¥6l¸víÚÍ›7™L&ѵ¨-¢¦«Ò"‘hÙ²ezzzwïÞ%º–þ‹Å_|ñ™LNHH ºõ“3 �`éÒ¥4íâÅ‹D×òa|>?88XKK+::šèZÔLÎ@‰D¢-[¶ ²}ûv�@@t9ïUVVæî¯¯´¥5L6N�:¥££3eÊ”ââb¢kéC||¼¡¡¡ƒƒƒJ­Š¥Öàµ5l¬^½:77—Çã999íÝ»—Ïç]Ñÿ”––úùùæææ:88]‘¦ :ºE 8p@WWw̘1çÏŸ'v�§ÚÚÚíÛ·3 {{{8ó:æ`r°W^^¾råJ …Âb±~ÿýw�§äÞ¾}»mÛ6]]]SSÓ¨¨(U~õ¥¾`rðRTT´jÕ*---##£ˆˆ%¼À‰‰‰sçÎ%“ÉfffìèèÀ{ÐA &_ÕÕÕ_}õº® ��ÝŽ;rss±=‹kmmMLL\¹r%:M‚©©ill,ŸÏÇpè]ð|éêêÞ¼yS(¬­­/\¸ðÃ? 2ÄÃÃÃÃÃã£�>rtt´°°�kŸB¡°¨¨(???'''33óéÓ§b±ØÍÍ�Éd655ÕÖÖæåå-^¼ŸoúxÇ'Ž$IPPzo›»»ûÝ»wi4Z~~~zzzFFƽ{÷*++FFF¶¶¶fffÖÖÖ¦¦¦C† ¡ÑhÚÚÚ4­­­M(¶µµµ¶¶–——×ÔÔ¼}û¶°°P P(”±cÇzyyyzzzzz2™Ì—/_º»»·´´ rîܹåË—ÝM“ƒ£;vìÛ·0|øð‡šššöz@CCC^^‡Ã)..®®®®¨¨¨©©immåóùíííÝÝÝ:::ZZZzzzúúú–––fffVVVvvvöööï~¨&%%ÅÏÏO$Ñéô»wïÂÙpDðÙ¢æŠ��EoCÖÓÓ{þü¹¼OG:‹/–÷‰ÒؘL&:�/„øN(.=z´jÕ*‰DB"‘¢££���•6ô¶mÛÂÂÂ555sçÎÅ{NêA &{oÞ¼™?>:É`dd¤ŸŸŸ’ øù矧OŸàp8AAA"‘HÉ 09koo÷÷÷¯©©„„„lÙ²Eù5P©Ôøøø1cÆ®]»¶cÇå× ñ`r°$‹—/_þâÅ À´iÓ”3CgŸŒ��“’’ÐåÚúé§“'OU‰¦‚ÉÁÒöíÛ#GŽüóÏ?µ´´,ÆÎÎ.66}6"""--�Àb4LfΞ={ðàA€¾¾þ•+W†JtE`Ö¬YhIÝÝÝ‹-*..&º"Í“ƒ�{÷î…‡‡Èdòü¡:SfnÞ¼yýúõ€††ÿ––¢+Ò09(++ûøã�ÑÏä>|xÞ¼yDWôG�Eç)((X²d z+4@09ÕÖÖæçç‡.ªºaâ+ê�B¡ÄÇÇÛÚÚnܸñå—_]‘&€É‘HœŸŸðòò:~ü8ÑõÍÐÐ0%%ÅÄÄEàE?�“3 [¶l¹zõ*€Åb%$${1M¶Q£FI/÷mÚ´éÎ�;DW¤Þ`rwúôitv\##£+W® �QežžžèQ±»»{ñâÅEEEDW¤Æ`r”‘‘�^³¢R©qqqè«ÕºqãF@cc£ŸŸ~«k<˜E¼~ýú“O> Ç•+u5þ|À«W¯à¥6…ÁäÈ­©©É××·¾¾ðÙgŸ­[·ŽèŠäÓó§[·n©]ý*&G>B¡pñâÅ………€Ù³gGFF]‘"ôôô¤w9œ:uêØ±cDW¤~`rä³iÓ¦Û·oÆŽƒÞ¦ŽFŽyùòeôS¥›7oNNN&º"5“#‡£G�þòË/ccã+W® w"«/ô�ô])‡CtEê&§¿nÞ¼ùÅ_¨Tê¥K—ÐO¿¨»U«V¡ßTkk«ôN¨?`rúåï¿ÿ–^†:vì›Í&º"Ì8pÀßßPZZ*½`}L·¡o}477¾üò˵k×]–zΔ�™™ /µõLÎôüd‹¯¯/: ”†ÑÕÕ½rå ºòáï¿ÿEtEj&ç6lØ�šš °··�‰‰!“ÉDW„‹áÇK/µmݺ5))‰èŠTLŽ,üïÿ `2™)))úúúDW„#ww÷sçÎ!"‹—-[–——GtE* &ç½RRR¾úê+�NOHH6lXSSÓ‰'f̘addÄ`0lll–-[öüùs\ËÈÊÊŠˆˆ°µµ¥Ñh¦¦¦.\�à33ëâÅ‹Ño¹­­Íßß¿¶¶V��<{ölÞ¼yzzzÞÞÞYYYX—©ˆž*QEq8œ!C†9þ<º144”B¡>|¸ªªª££###ÃÞÞ§užÑŸŽ¯¯/ÀÛÛûùóç<¯¤¤$((ðÅ_`>"J,/Y²}êÔ©]]]r==''‡Á`,Y²¤²²²®®níÚµ åÆ�8UK ˜œ>ÔÕÕ�=ýíÙ¹s§t{hhhXXXÏG>{ö `ccƒy ÒäP(”ÆÆFév>ŸollL£Ñäý�î¿ÎÎΉ'¢¬X±¢ÿO‰Dæææ���è¡PÈb±¬­­ñ«–(09½ñù|///ô÷æã�?‰D²Ï`0H$’X,ƶ ´€>ç•?~< ¹¹Û{ª¨¨°´´DkØ¿?Ÿ…^JÙ¸qcÏ�»wï\ºt ‡2‰_çô¶fÍšôôt€‹‹Ë¹sçH$Y-êèèàñxŽŽŽèäëJÐÜÜ\TTäâ₞LâÄÂÂ"11Q[[ð¯ý �DîƒîÞ½ ˜0aBÏ�è—š÷T˜œØ·oßùóçæææ‰‰‰:::²PÎô³­­­YYYþþþfffçÎ�Ã{8WW׳gÏ¢—Ú‚ƒƒ=zôÁ§üý÷ß++«žÑczw¹F!ú §B._¾Œa ƃ>øøêêj&“¹fÍ<ŠA:Ò³µ½{÷¢[Ølö‹/ð±Oß~û-:®……—Ë•ý`@NNNÏ�èg¶?úè#<Ë$<æüϳgÏ>ýôS±XŒ È©S§&Mš$ûñ sæÌa³ÙÊ™GfçÎ�|>¿  ÀÎÎÎÅÅE$¼}ûí·ÁÁÁ€ÊÊÊ tvvÊ»‰DPÚÙ¬ò]•PUUemm�6ä»ï¾ûàãÛÛÛ]]]ƒƒƒ±]+·'´˜>¯nݺ…Óнðx¼É“'£õʸ¸sçNÏ�OŸ>Ìœ9ÿJ• s@WWW@@@yy9 00pçÎ�²/ ---Ïž=KÈÍ8è‚<èlUJ@§Óÿúë/ô/K||ü÷ßÿ¾GÚÙÙ¸\nÏ�u™á¤ÿ{r$IhhhNNÀÕÕõÌ™3<¯çóùqqqÒ„Ž3݃r w—566*mD333éõ’o¿ý6&&¦Ï‡¡Ë]=~ü¸çFôKõšä¤_ˆ>èL®WÀèã'OžÜÖÖÖsãèÑ£³³³±- ­ŠÅb-[¶¬×¡‹N=zÛ?èƒWPD"‘½½½……�ÇC·…±cÇZ[[K·hŒA�œøøxôÃ`0rss?øøßÿý}€ðK‚ {öì)--íêê*--Eg…vuu•¾O¯LÒS5ssóòòòw���M§Ó—.]ZUUU__N¡P®_¿®üRñ6x“óøñcô�>‰ÔÏÏd,R€Sr~ûí·Ù³g�1BKKKWW×ÕÕõÇ$$6‰D,úé§hm...íííï>æÉ“'¾¾¾úúúººº3f̸wïžòëT‚AšœŠŠ évý¿»D™ÐÚXÕoòÞ�¤©ã�·páBôЊ+¶oßNtEêDKKëÒ¥K£F�\¾|½-mtÉ‘H$!!!¹¹¹wwwôƒk�\LLL’’’Ðç¾ÿþûèèh¢+"À KÎÎ�;ccc#FŒHHH@¯ðBò²··¿xñ"™L–H$kÖ¬ÉÎÎ&º"e\ɉ‹‹ûñÇÁÿMkjjJtEjÌ××m&úVòÛ·o‰®H©Qrîß¿�~N �'ÉÉɉèŠÔÞ¶mÛÂÂÂ555sçÎmmm%º"å,Éyóæ�t܃¢7°@÷óÏ?£·p8œ   ‘HDtEJ2(’ÓÞÞîïï_SS ùüóω®HsP©Ôøøxt®àk×®)ç£Jª@ó“ƒÎ�ôâÅ À´iÓàⲘ366NJJBç§ÿé§ŸNžH£’“žžŽ¾]C¥Roß¾Mt9r[¿~½Œ·›îÞ½Kt�rÛ¸q#Z<‹Åê9¯¼PãädffîØ±ƒÏç£_–”” çÖ€_~ù…ØÚ“‘‘ñ¾Ø˜˜˜à7·~„BáüùóÑoÁÇǧ»»Ýþúõëõë×·¶¶[Þ@¨qr<==õõõ���Ò½>ÿüs¢KS�X,¶°°x76T*uË–-DW§ ÖÖVGGGôAgÎÌÌD_|ªé8”º&§¬¬ �¶†B¡ 6LúÔ××Wÿ6KmÛ¶�J¥¾žþLÍ£²Š‹‹¥§aaa …L&#¢Ö“M«krvïÞ-ý £P(t:ÝÔÔtìØ±MMMD—6 OžŸ___ïïï�Þ±«¾\\\Ð;ö¥¨TjHHQõ`eüøñ“&M*))ÿ7G;€J¥ž={–к€èè*â}·â"²víZ�@@t�²gÏž^'l‡è¢„Ëå:;;K'îÉØØXzÙ@½¨erV®\Ù狉Dš5kÑHqq±ô|A'''¢+ôšçû~^€ääd¢kT„ú�­µ··ÇÅÅIOÕz"“Éï»<¥FF�=nÜ8éõ�+V]Ñ€P©T±XÜçÿR(”Ó§O+¹$l]¹�>}ºÏµ;I$Ò¨Q£nÞ¼It�ø÷¿ÿ�žÛ RVVFt9ÕÝÝ}øða�>�< ¥®®Žèå¦~Éqwwï•*•ª££³ÿ~黢ꮢ¢AAÜÜ܈®3UUUkÖ¬A¤×  …òóÏ?]�ÜÔ,9¯_¿îyM]øiÙ²eÕÕÕD—†1ô}Þ'N]Æ>|èââÒóo‚ ãÆ�#º.¹á{¯tmmm~~>‡Ã)))©¬¬¬¬¬¬­­mnn …íííÝÝÝ:::ZZZÚÚÚC† ±°°°°°°´´´³³spp°··g0½vøÍ7ßìß¿}‘C"‘ÆŽû믿N�:¿oW2úÓÚÚ*‰´µµi4Zÿû£Äbñ… 6mÚÔÙÙ)}½úüùsggç^�Äü÷C'G"‘äçç§¥¥edddff¢5³X,sssKKK&“ihhH&“Ñïýþy<^SSSEEEUUUyyyaaaWW ///OOO6›Íd2Åb±µµuee%•Je0û÷ï #d½A…õ¿?ÚÚÚ­­­ £ÿý!ú›“O]]Ý×_}úôi …" ?ÿüóC‡áúûƒñ7€É‘K(¦¦¦nÚ´ ]MÒÀÀÀÏÏ/22òöíÛ œG …ÂW¯^]ºtiÛ¶m“'O¦P($ÉÍÍ �NA�ÐÐPõzM©œþ8p ¤¤�úñóàÁôCGúúú6lP£þ 49•••{÷î6lÀÁÁa×®]�=ÂvM•¶¶¶+W®„„„èéé\]]/^¼¨.”ÙcccAØl¶zõç»ï¾322ØÙÙ©QOΫW¯V¬X�^­ßºukAAÁKù ¡P˜’’²páB …Âd2úé§^ëuªØÙÔ½?Š$çÍ›7Ë—/'“ÉvvvgÏžíêêRxxÅp¹ÜíÛ·ëêê:422RÕþ¾ÂþȦý‘/9�`ÿþý:::666ÑÑÑÄ®t‡¾ÄÔÖÖ;vì�;w¬D öG6Mê�Éáp8ÎÎÎ cïÞ½Êÿ;ñ>¥¥¥þþþ€5kÖtttX ì�lÖŸþ&çäÉ“ÚÚÚnnnªyõæÏ?ÿ422²··ÏËË#¤ØÙ4¯?NŽH$Ú¼y3‰Dúúë¯Uù†ð·oßzxxèééݸqC™ãÂþȦ©ýù@rø|þâÅ‹i4Zll,åáK |úé§T*õÂ… ÊöG6 î�¬äˆD¢   }}ýÔÔT̪ÙX,þòË/ÉdòŸþ‰÷X°?²ivd%gÓ¦M4M§ùÚ°a�NOKKÃuØÙ4»?ïMÎü� H||<…áN$-Z´ˆÉdâw5ì�lߟ¾“óöí[CCÃÍ›7ãVîÚÚÚlmmgÏž-‹1ß9ì�lƒ¡?}'gþüùŽŽŽ<ÏÚp÷àÁ2™�ùžad ýé#9©©©¥},ùéÓ§sçÎ2dˆ®®îÌ™3ïÝ»‡áÎCCCGŒ�í�PÉý‘H$ÉÉÉ666d2ó=«uùå—éÓ§Òéô1cÆ?{ö Ã!dô§�äxzzΙ3ÃáeÈÉÉa0K–,©¬¬¬««[»v-…BÁð .—Ë`0°�…U™ý)..öóósvvÖ××Ç#9jÝŸÐÐP …røð᪪ªŽŽŽŒŒ {{{2™œ��€Õ2úÓ;9¯^½BäúõëX�-ƒH$rpp077ïììD·…B‹emm�áÝ+V¬˜0aV{Sf$IPPÐ�?þØÝÝmii‰Gr$êÜŸÐÐа°°ž[ž={°±±Áp”÷õ§wrvìØamm­œ©™ÑÃúÆ�{nܽ{7àÒ¥KX�’žžÀê®eöG"‘Hÿ¦à—µîÏ» ‰DÂð²ÇûúÓ{ú¥;wîøûû+ç#ÊwïÞL˜0¡çFôË;wî`5Ê´iÓLLL°Ú¡2ûPÂLjÝŸ^:::x<ž££cÏi^è}ýùGrø|þÓ§OÝÜÜ0R(ÆÆÆúøø˜™™1 ''§#GŽôœ±îï¿ÿXYYõ|–¥¥% °°“‚L™2åþýûß•’û£šÔŸøøxÀŽ;0)õÞþô<åååòóó19Ì%%%öíÛרØXWWwôèQ‰´uëVé|||999=ŸUTTÀv}ˆ�;w:88 |?JîOOø�­I4¢?‰¤ººšÉd¢Kô`«Ïþü#9è �ÚÚZLÆKJJb³Ù=·,_¾œJ¥¶´´ _ö™ôhãêêŠI ¨£G�2™Ì�ïGÉýé ×äh@êëëÇ�¿dÉ<^bõÙŸœ­577°ZHcþüùh+¥Æ�×ÝÝÍápÐ/Ñ�:::z>ýÛÅ<ŒŒŒššš¾%÷GiÔ½?³g϶··�ŽŽÆã%VŸýùÇ<¥ÚÚÚ€ÎÎÎ!C† |¼–––C‡%$$p¹\´§¨ÎÎNôvvv.—ÛóYéÒ…˜hkkÓÕÕø~”Ü¥Qëþ…ÂÀÀ@KK˳gÏâte¢Ïþü㘃.IW__�Éx~~~{÷î]»vmaa!z•0** ôXxhúôé€Ç�÷|úåÌ™31©U__/]mo ”Ü¥Qëþ„‡‡óùü¸¸8étÕcÆŒÉÉÉÁ¤TŸýùGr† † &×µD"QVV–™™Ù¦M›†Š^%äñx=ãååeooéÒ¥®®.é³bbb¬­­çÍ›7𤊋‹ÑÏHÉýQõíÏîÝ»9Nbb"�Fø ïÓgzsl`�TIDATll²³³>™Lf³ÙÕÕÕ‘‘‘õõõ</55õĉÿ›D:uêTcccHHHuuuCCCDDDQQÑÉ“'étúÀk�ÊÊÊÂdîi%÷GiÔ´?gΜٳgσôôô�Ð51Ôwz]1X½z5V+OÔÕÕ…‡‡[[[S©T&“¹jÕª¯¾ú ´ç¥³'OžøúúêëëëêêΘ1Û;>%Iii)‚ XÝ §äþ Wf{9yò$& Ô·?2NL²³³1©AòþþôNÎÍ›7�:¯ü®o¾ù†Édbµx(ì�lƒ§?½“#‰†þÙgŸ)«0|ñù|kkë/¿ü«ÂþÈ6xúÓǧ Ž9B§Óß¼yƒa¸;|ø0�Nûö-†û„ý‘m�ô§�äðù|ôCBø†¯†† ÿ ¢`d$ýéûÓÔW®\A$..ÏÂp÷ñÇ[YY555a¾gØÙCÞ;÷Íúõë _¿~�[aø:vì™LÆo¦/ØÙ4¾?ïMNgg§‹‹‹­­-V7ð)SRR…BÙ³g~CÀþȦñý‘5SaUUÕèÑ£'NœˆÇ?iiiÚÚÚkÖ¬Ác>¤ž`dÓìþ|`^éââbkkkGGGl/¿àçÒ¥Kt:=00P9è…ý‘Mƒûóáµ ÊËË­¬¬z}�FÕˆÅâ�H¤ˆˆe~öG6MíO¿ÖÏijjòõõ¥R©û÷ï'v�­÷©­­E+DWW2ØÙ4²?ý]yJ,GFFR©ÔiÓ¦µzQŸÄbñ¹sç˜Læˆ#0¼[I�2`d—¡aý‘o�Ð'OžLš4‰J¥nÙ²¥¾¾^Î"±÷èÑ#OOO2™¼~ýzUx û#›&õGE"ѯ¿þ:tèP==½ýë_D}ÿ¹¹¹óçÏGÄÍÍíÑ£G„ÔÐ'ØÙ4¦?Ьê.‘HÚÛÛ>lnnN£Ñoݺ¥Ø~äÕÕÕçíí� ȸqãâââð¾´ªØÙ4 ? &ÕÞÞ~âĉñãÇìíí¿ùæ›çÏŸd‡ïÓÕÕ•œœ¼zõj###*•ºhÑ¢Û·oã1¶`dSëþ (9R9997nDç5jÔêÕ«Ïœ93ÀUˆANNNdd¤ŸŸŸ¾¾>:a\dddUU&5+ì�lêØD‚Ý|‰äáÇÉÉÉééé>ìêê200ppppttd±XfffVVVL&sÈ�!ZZZ:::ZZZ� £££¹¹™ËåVWW———s8‡óêÕ«îîn&“9mÚ´éÓ§ûûû÷š TíÀþȦ^ýÁ29=ñùüÜÜܼ¼¼¼¼<‡SRRRSS# e?K[[ÛÊÊŠÅb9:::99¹¸¸ 3KiØÙT¿?x%ç]b±¸¦¦¦¦¦¦µµU ´··www£9tuuõõõ---1™§KMõêϺuë$‰««kXXìP½ßå%’ :OÒâÅ‹ccc‰®êCïU@ ê˜RL)&‚“AŠ€É� EÀä@�"`r H09¤˜RL)&‚“AŠ€É� EÀä@�"`r H09¤˜RL)&‚“AŠ€É� EÀä@�"`rÔÀ³gÏæÍ›g`` §§çíí�••EtELŽÊ{ðà�»»»žž^AAAiié¨Q£ØlöÍ›7‰®k°ƒs|ª(tŽÏÀÀÀ—/_666–””0 €H$rppèìì,**¢ÑhD—9xÁcŽJ«««ãp8‹-Bc “ÉAAAåååW¯^%¶¶A&G¥ÕÖÖ&L˜Ðs#úå�;wˆ© À䨸¶¶6@¯…_,--………ÄÔ`rTœ@ èèèôܨ«« hjj"¦&“£ŽÐ‹:è%ˆ(09*MKK ÐÑÑÑs#ú¥��15A˜§§§àr¹=7VTTlmm‰© Àä¨8SSSÀãÇ�{nD¿œ9s&15AøN¨Ê’¾Êápš››KJJèt:@$999µ··¢[ BÀcŽJCäÔ©S���!!!ÕÕÕ EEE'Ož„±!LŽª›2eÊýû÷[ZZX,Öˆ#ŠŠŠÒÒÒfÏžMt]ƒ…è sqq¹víÑU@ÿ�9¤˜RL)&‚“AŠ€É� EÀä@�"`r H09¤˜RL)&‚“AŠ€É� EÀO¨�¦¦¦^sAutt¼~ýý·••:¡¤ à§©Uˆ±±qccãûþwýúõÇ�Wf=� ðlM…Lš4‰Dêû'‚ ȤI“”\$LŽ Y¾|ùûþ‹B¡(³H6˜²páÂ>_ÉP(”ùóç2Dù%Aï“£Btttüýý©Tj¯í"‘HÆá"LŽjY¶lYwww¯� Ã××—�z ÷�ÉQ-sæÌÑ××ï¹…J¥.^¼Xºò¤"`rT‹––V```϶îîîàà`K‚úßÏQ9wïÞí9g´¡¡amm-…ß³V-ð˜£rØlöСCÑkii}úé§06*&Gå�H¤åË—£'l� ((ˆèŠ >À³5U”››‹Þ1`iiY^^—gSA𘣊&Nœ8räHÀªU«`lT<�&^uuuIIIeeeeeemmmss³P(DWÕÍÏÏg0–––¶¶¶½ÖÜ…”ž­ ¦¦&###333/////¯¡¡@"‘˜L&“É444$“ÉT*µººÚÜÜ\ ðx¼¦¦¦ŠŠŠ––ô‘#FŒprrrqqa³Ù“'O†ké(LŽ’ˆÅâììì¿þú+99¹  €B¡¸¸¸¸¸¸8:::88°X,&“ùÁkh���ååå/_¾äp8ùùù999oÞ¼¡Ñhnnn . 6l˜r¾&wEEE¿þúëüQUUekk»`Á‚éÓ§{xx «çЛ7oÒÓÓoݺuõêÕ––– &„††c²sH „›””oooA†¾{÷î¼¼<üÆâóùׯ__¹r%ƒÁÐÓÓ[¿~ýëׯñ‚ÉÁEJJÊ”)S¾¾¾IIIB¡PiC766FEE�=šJ¥†††Âüà&c%%%sçÎx{{?xð€¨2D"Q\\œ­­-•JÝ´iS{{;Q•h*˜Ì…Â~ø�N§;99eff]ŽD"‘twwÿûßÿÖÓÓ=ztjj*Ñåh˜lp¹\6›M£Ñ8 ˆ.ç¸\îÂ… Édò7ß|£ÌóFÍ“ƒ�û÷˜°X¬§OŸ]Ë{?~œN§Ïœ9³¹¹™èZ4¼*=PIIIK—.õööŽŽŽÖÕÕ%ºYž={6oÞ¼¡C‡¦¤¤˜››]Žzƒ÷­ HbbâǼ|ùòË—/«xlãÇ�¿ÿ¾@ ðôô¬©©!ºõ�9ŠËÈȘ3gΪU«ÔkÁÚÚZô}ØÔÔÔ^ŸÜ†ú&GAåååãÆ�›9sfLL ™L&ºù”––N�:uâĉýõ¼[109Š‹ÅÞÞÞ555¹¹¹ÚÚÚD—£ˆ¬¬,6›}øðለ¢kQKðuŽ"Ž9’��}ñâE5� `êÔ©»víÚºu«tÆwH>Ä^ÚSG���FFF»víÂoˆääd2™Œß‰D(:88,Y²×Q4LŽÜ¶mÛfjjÚÚÚŠÇ΋‹‹ýüüœ��õõõñNŽD"A_ç&°`Á''§_ýU ci¢£«fâããÉd2—ËÅiÿ���è?,--•pÌ‘H$‡Ö×ׇ·„Ê sä“””äååeii‰Óþ•? nPPP[[[zzº’ÇUw09òÉÊÊš6mšbÏ …±±±>>>fff ÃÉÉéÈ‘#b±Xù;éÉÔÔ”ÅbÝ¿_á= RDôÔ ºáµk×{zRR`ß¾}���uuuG�%‘H[·níóÁï;[“k'ý´råÊY³f dƒLŽ /^¼PìéIIIl6»çt.Ï–––w,#9ýßI?íØ±cܸq ?}p‚gkr@§w266VìéóçÏOMMí¹eܸqÝÝÝGÉ;éÅØØ¸¾¾^á§Np¦B9 ·xI½_©¥¥åСC \.·¹¹Yº½³³SÉ;éE"‘À»×ä�9r@�6è‘G~~~{÷î]»vmaa¡X,–H$QQQ@Î(b²“^LLL~úà“#&“‰ —ËUà¹"‘(++ËÌÌlÓ¦MC‡EÿÆóx<åïä]L&s€;l`rä```0zôè(ð\2™Ìf³«««###ëëëy<^jjê‰'”¿“weggOœ8q€;tˆ¼<¡†V®\9}útÅž[WWnmmM¥R™LæªU«¾úê+ô§àêêŠ>½èÜËÉ“'åÚ‰\jjjINNVì›´àçsäséÒ¥%K–”••Y[[] 6¢¢¢vïÞ]UU¥¾Ÿ˜ <[“�¿¿¿‘‘ѹsçˆ.3§OŸ^ºt)Œ�¼`r䣥¥zäÈ‘ÖÖV¢kÁ@BB‡ÃY·nÑ…¨x¶&·æææ1cÆ„‡‡ÿðÃD×2 B¡ÐÙÙyüøñüñѵ¨xÌ‘›��ÁŽ;:ôìÙ3¢k�ï¿ÿ¾´´ôûï¿'ºµ�9Š‹Å>>>UUU�=RÓW™™™Ó§O?räœÁC109 *//?~<›ÍŽ‹‹S»Y£^¿~=uêÔÉ“''$$ÀûnÏÖdmm�˜˜˜’’¢v³kjjfÏžmiiyþüy…Áä(ÎÃÃ#&&æÔ©SkÖ¬ …D—Ó/eee^^^‚\»v ®ˆ809âïïÿ×_ÅÄÄ´··]Îtuu}òä Ñå¼×±cǦN�êää”––2À±7ÿhŒÊÊÊ3fÐh´ýû÷«ÚÊSååå , “É»wï†+Oa&3"‘hß¾} ÃÁÁ!--�èr$‰D >> import anytree >>> anytree.config.ASSERTIONS = True anytree-2.13.0/docs/tricks/multidim.rst000066400000000000000000000041431500063244000200450ustar00rootroot00000000000000Multidimensional Trees ====================== **Application**: Tree nodes should be hooked-up in multiple trees. An anytree node is only able to be part of **one** tree, not multiple. The following example shows how to handle this. **Example**: 4 objects `A`, `B`, `C` and `D` shall be part of the trees `X` and `Y`. The objects `A`, `B`, `C` and `D` are instances of a class `Item`. It is *not* a tree node. It just contains references `x` and `y` to the node representations in the corresponding trees. >>> class Item: ... def __init__(self, name): ... self.name = name ... self.x = None ... self.y = None ... def __repr__(self): ... return "Item(%r)" % self.name >>> a = Item('A') >>> b = Item('B') >>> c = Item('C') >>> d = Item('D') The tree nodes just contain the reference to `item` and take care of the proper reference, by using the attach/detach protocol. >>> from anytree import NodeMixin, RenderTree >>> class NodeX(NodeMixin): ... def __init__(self, item, parent=None): ... self.item = item ... self.parent = parent ... def _pre_detach(self, parent): ... self.item.x = None ... def _pre_attach(self, parent): ... self.item.x = self >>> class NodeY(NodeMixin): ... def __init__(self, item, parent=None): ... self.item = item ... self.parent = parent ... def _pre_detach(self, parent): ... self.item.y = None ... def _pre_attach(self, parent): ... self.item.y = self Tree generation is simple: >>> # X >>> xa = NodeX(a) >>> xb = NodeX(b, parent=xa) >>> xc = NodeX(c, parent=xa) >>> xd = NodeX(d, parent=xc) >>> # Y >>> yd = NodeY(d) >>> yc = NodeY(c, parent=yd) >>> yb = NodeY(b, parent=yd) >>> ya = NodeY(a, parent=yb) All tree functions as rendering and exporting can be used as usual: >>> for row in RenderTree(xa): ... print("%s%s" % (row.pre, row.node.item)) Item('A') ├── Item('B') └── Item('C') └── Item('D') >>> for row in RenderTree(yd): ... print("%s%s" % (row.pre, row.node.item)) Item('D') ├── Item('C') └── Item('B') └── Item('A') anytree-2.13.0/docs/tricks/readonly.rst000066400000000000000000000062721500063244000200430ustar00rootroot00000000000000Read-only Tree ============== **Application**: A read-only tree data structure, which denies modifications. The `Node._pre_attach` and `Node._pre_detach` hookups can be used for blocking tree modifications. If they raise an `Exception`, the tree is not modified. >>> from anytree import NodeMixin, RenderTree The exception: >>> class ReadOnlyError(RuntimeError): ... pass Permanent --------- The read-only attribute needs to be set after attaching to parent: >>> class ReadOnlyNode(NodeMixin): ... ... def __init__(self, foo, parent=None): ... super(ReadOnlyNode, self).__init__() ... self.foo = foo ... self.__readonly = False ... self.parent = parent ... self.__readonly = True ... ... def _pre_attach(self, parent): ... if self.__readonly: ... raise ReadOnlyError() ... ... def _pre_detach(self, parent): ... raise ReadOnlyError() An example tree: >>> a = ReadOnlyNode("a") >>> a0 = ReadOnlyNode("a0", parent=a) >>> a1 = ReadOnlyNode("a1", parent=a) >>> a1a = ReadOnlyNode("a1a", parent=a1) >>> a2 = ReadOnlyNode("a2", parent=a) >>> print(RenderTree(a).by_attr("foo")) a ├── a0 ├── a1 │ └── a1a └── a2 Modifications raise an `ReadOnlyError` >>> a0.parent = a2 Traceback (most recent call last): ... ReadOnlyError >>> a.children = [a1] Traceback (most recent call last): ... ReadOnlyError The tree structure is untouched: >>> print(RenderTree(a).by_attr("foo")) a ├── a0 ├── a1 │ └── a1a └── a2 .. note:: It is important to use the ``_pre_*`` and **not** the ``_post_*`` methods. An exception raised by `_pre_detach(parent)` and `_pre_attach(parent)` will **prevent** the tree structure to be updated. The node keeps the old state. An exception raised by `_post_detach(parent)` and `_post_attach(parent)` does **not rollback** the tree structure modification. Temporary --------- To select the read-only mode temporarily, the root node should provide an attribute for all child nodes, set *after* construction. >>> class ReadOnlyNode(NodeMixin): ... def __init__(self, foo, parent=None): ... super(ReadOnlyNode, self).__init__() ... self.readonly = False ... self.foo = foo ... self.parent = parent ... def _pre_attach(self, parent): ... if self.root.readonly: ... raise ReadOnlyError() ... def _pre_detach(self, parent): ... if self.root.readonly: ... raise ReadOnlyError() An example tree: >>> a = ReadOnlyNode("a") >>> a0 = ReadOnlyNode("a0", parent=a) >>> a1 = ReadOnlyNode("a1", parent=a) >>> a1a = ReadOnlyNode("a1a", parent=a1) >>> a2 = ReadOnlyNode("a2", parent=a) >>> print(RenderTree(a).by_attr("foo")) a ├── a0 ├── a1 │ └── a1a └── a2 Switch to read-only mode: >>> a.readonly = True >>> a0.parent = a2 Traceback (most recent call last): ... ReadOnlyError >>> a.children = [a1] Traceback (most recent call last): ... ReadOnlyError Disable read-only mode: >>> a.readonly = False Modifications are allowed now: >>> a0.parent = a2 >>> print(RenderTree(a).by_attr("foo")) a ├── a1 │ └── a1a └── a2 └── a0 anytree-2.13.0/docs/tricks/weightededges.rst000066400000000000000000000025221500063244000210300ustar00rootroot00000000000000Weighted Edges ============== **Application**: Add weight to edges and make use of them. As every node has just one parent, the easiest way to handle edge weights is to store them in the child node. If a child node is detached from its parent, the weight attribute can be cleared automatically by `Node._post_detach`. >>> from anytree import NodeMixin, RenderTree >>> class WNode(NodeMixin): ... ... def __init__(self, foo, parent=None, weight=None): ... super(WNode, self).__init__() ... self.foo = foo ... self.parent = parent ... self.weight = weight if parent is not None else None ... ... def _post_detach(self, parent): ... self.weight = None An example tree: >>> a = WNode("a") >>> a0 = WNode("a0", parent=a, weight=2) >>> a1 = WNode("a1", parent=a, weight=3) >>> a1a = WNode("a1a", parent=a1) >>> a2 = WNode("a2", parent=a) >>> for pre, _, node in RenderTree(a): ... print("%s%s (%s)" % (pre, node.foo, node.weight or 0)) a (0) ├── a0 (2) ├── a1 (3) │ └── a1a (0) └── a2 (0) >>> from anytree.exporter import DotExporter >>> DotExporter(a, ... nodenamefunc=lambda node: node.foo, ... edgeattrfunc=lambda parent, child: "style=bold,label=%d" % (child.weight or 0) ... ).to_picture("weight.png") # doctest: +SKIP .. image:: ../static/weight.png anytree-2.13.0/docs/tricks/yaml.rst000066400000000000000000000047211500063244000171650ustar00rootroot00000000000000YAML Import/Export ================== YAML_ (YAML Ain't Markup Language) is a human-readable data serialization language. PYYAML_ implements importer and exporter in python. *Please install it, before continuing* .. note:: anytree package does not depend on any external packages. It does **NOT** include PYYAML_. .. Warning:: It is not safe to call yaml.load with any data received from an untrusted source! yaml.load is as powerful as pickle.load and so may call any Python function. The yaml.safe_load function limits the load functionality to built-in types. Export ------ The :any:`DictExporter` converts any tree to a dictionary, which can be handled by `yaml.dump`. >>> import yaml >>> from anytree import AnyNode >>> from anytree.exporter import DictExporter Example tree: >>> root = AnyNode(a="root") >>> s0 = AnyNode(a="sub0", parent=root) >>> s0a = AnyNode(a="sub0A", b="foo", parent=s0) >>> s0b = AnyNode(a="sub0B", parent=s0) >>> s1 = AnyNode(a="sub1", parent=root) Export to dictionary and convert to YAML: >>> dct = DictExporter().export(root) >>> print(yaml.dump(dct, default_flow_style=False)) a: root children: - a: sub0 children: - a: sub0A b: foo - a: sub0B - a: sub1 :any:`DictExporter` controls the content. `yaml.dump` controls the YAML related stuff. To dump to a file, use an file object as second argument: >>> with open("/path/to/file", "w") as file: # doctest: +SKIP ... yaml.dump(data, file) Import ------ The `yaml.load` function reads YAML data --- a dictionary, which :any:`DictImporter` converts to a tree. >>> import yaml >>> from anytree.importer import DictImporter >>> from pprint import pprint # just for nice printing >>> from anytree import RenderTree # just for nice printing Example data: >>> data = """ ... a: root ... children: ... - a: sub0 ... children: ... - a: sub0A ... b: foo ... - a: sub0B ... - a: sub1 ... """ Import to dictionary and convert to tree: >>> dct = yaml.load(data, Loader=yaml.Loader) >>> pprint(dct) {'a': 'root', 'children': [{'a': 'sub0', 'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]}, {'a': 'sub1'}]} >>> root = DictImporter().import_(dct) >>> print(RenderTree(root)) AnyNode(a='root') ├── AnyNode(a='sub0') │ ├── AnyNode(a='sub0A', b='foo') │ └── AnyNode(a='sub0B') └── AnyNode(a='sub1') .. _YAML: https://en.wikipedia.org/wiki/YAML .. _PYYAML: http://pyyaml.org/wiki/PyYAMLDocumentation anytree-2.13.0/pyproject.toml000066400000000000000000000071761500063244000161650ustar00rootroot00000000000000 [project] name = "anytree" dynamic = ["version"] description = "Powerful and Lightweight Python Tree Data Structure with various plugins" authors = [{ name = "c0fec0de", email = "c0fec0de@gmail.com" }] dependencies = [] requires-python = ">=3.9.2,<4.0" readme = "README.rst" license = "Apache-2.0" keywords = ["tree", "tree data", "treelib", "tree walk", "tree structure"] classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] [project.urls] "Homepage" = "https://github.com/c0fec0de/anytree" "Documentation" = "https://anytree.readthedocs.io/en/stable/" "Bug Tracker" = "https://github.com/c0fec0de/anytree/issues" [dependency-groups] dev = [ "coveralls>=3.3.1", "mypy>=1.9.0", "pytest-cov>=5.0.0", "ruff>=0.11.2", "pre-commit>=4.2.0", "sphinx>=5.3.0", "sphinx-rtd-theme>=2.0.0", "sphinxemoji>=0.3.1", "test2ref>=0.4.2", ] [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" [tool.pdm.build] includes = ["src/anytree"] [tool.pdm.version] source = "scm" fallback_version = "0.0.0" [tool.ruff] line-length = 120 fix = true target-version = "py37" [tool.ruff.lint] select = [ # isort "I", # pyflakes "F", # pycodestyle "E", "W", # pydocstyle "D", # pep8-naming "N", # flake8-2020 "YTT", # flake8-bugbear "B", # flake8-quotes "Q", # flake8-debugger "T10", # flake8-gettext "INT", # pylint "PL", # misc lints "PIE", # flake8-pyi # "PYI", # tidy imports "TID", # implicit string concatenation "ISC", # type-checking imports "TC", # comprehensions # "C", "C4", # pygrep-hooks "PGH", # Ruff-specific rules "RUF", # flake8-bandit: exec-builtin "S102", # numpy-legacy-random "NPY002", # Perflint # "PERF", # flynt "FLY", # flake8-logging-format "G", # pyupgrade "UP", # annotation # "ANN" # bandit # "S", # Argument # "ARG", # Pathlib # "PTH", # Simplify # "SIM", # Return "RET", # Raise "RSE", ] ignore = [ # too-many-arguments 'PLR0913', # Ambiguous doc "D100", "D101", 'D203', 'D212', 'D402', 'D406', # Multiline doc is ok. 'D200', # We use the class for constructor doc 'D107', # Magic Method Documentation is optional 'D105', # We use faster isinstance checks 'UP038', # Assert 'S101', # Type-Checking 'TC001', # Indent of doctest 'D412', # unused loop 'B007', ] [tool.ruff.lint.per-file-ignores] "tests/*" = [ # Magic Values "PLR2004", # Argument "ARG001", # doc "D100", "D101", "D102", "D103", # Unused variables "F841", # complexity 'PLR0915', ] [tool.ruff.lint.pydocstyle] convention = "google" [tool.pytest.ini_options] filterwarnings = [ "ignore::DeprecationWarning", ] addopts = """ --cov src/anytree --cov-report term --cov-report html --doctest-glob=docs/*.rst --doctest-modules --ignore-glob=tests/testdata/* --log-level=INFO --junitxml=report.xml """ [tool.coverage.report] exclude_lines = [ 'return NotImplemented', 'raise NotImplementedError', 'pragma: no cover', 'assert False', ] [tool.mypy] disable_error_code = "misc" ignore_missing_imports = true anytree-2.13.0/src/000077500000000000000000000000001500063244000140255ustar00rootroot00000000000000anytree-2.13.0/src/anytree/000077500000000000000000000000001500063244000154745ustar00rootroot00000000000000anytree-2.13.0/src/anytree/__init__.py000066400000000000000000000027661500063244000176200ustar00rootroot00000000000000"""Powerful and Lightweight Python Tree Data Structure.""" __version__ = "2.12.1" __author__ = "c0fec0de" __author_email__ = "c0fec0de@gmail.com" __description__ = """Powerful and Lightweight Python Tree Data Structure.""" __url__ = "https://github.com/c0fec0de/anytree" from . import cachedsearch, util from .iterators import LevelOrderGroupIter, LevelOrderIter, PostOrderIter, PreOrderIter, ZigZagGroupIter from .node import AnyNode, LightNodeMixin, LoopError, Node, NodeMixin, SymlinkNode, SymlinkNodeMixin, TreeError from .render import AbstractStyle, AsciiStyle, ContRoundStyle, ContStyle, DoubleStyle, RenderTree from .resolver import ChildResolverError, Resolver, ResolverError, RootResolverError from .search import CountError, find, find_by_attr, findall, findall_by_attr from .walker import Walker, WalkError # legacy LevelGroupOrderIter = LevelOrderGroupIter __all__ = [ "AbstractStyle", "AnyNode", "AsciiStyle", "ChildResolverError", "ContRoundStyle", "ContStyle", "CountError", "DoubleStyle", "LevelGroupOrderIter", "LevelOrderGroupIter", "LevelOrderIter", "LightNodeMixin", "LoopError", "Node", "NodeMixin", "PostOrderIter", "PreOrderIter", "RenderTree", "Resolver", "ResolverError", "RootResolverError", "SymlinkNode", "SymlinkNodeMixin", "TreeError", "WalkError", "Walker", "ZigZagGroupIter", "cachedsearch", "find", "find_by_attr", "findall", "findall_by_attr", "util", ] anytree-2.13.0/src/anytree/cachedsearch.py000066400000000000000000000031061500063244000204430ustar00rootroot00000000000000""" Node Searching with Cache. .. note:: These functions require https://pypi.org/project/fastcache/, otherwise caching is not active. """ from . import search # fastcache is optional try: from fastcache import clru_cache as _cache except ImportError: from functools import wraps # dummy decorator which does NOT cache def _cache(size): # pylint: disable=W0613 def decorator(func): @wraps(func) def wrapped(*args, **kwargs): return func(*args, **kwargs) return wrapped return decorator CACHE_SIZE = 32 @_cache(CACHE_SIZE) def findall(node, filter_=None, stop=None, maxlevel=None, mincount=None, maxcount=None): """Identical to :any:`search.findall` but cached.""" return search.findall(node, filter_=filter_, stop=stop, maxlevel=maxlevel, mincount=mincount, maxcount=maxcount) @_cache(CACHE_SIZE) def findall_by_attr(node, value, name="name", maxlevel=None, mincount=None, maxcount=None): """Identical to :any:`search.findall_by_attr` but cached.""" return search.findall_by_attr(node, value, name=name, maxlevel=maxlevel, mincount=mincount, maxcount=maxcount) @_cache(CACHE_SIZE) def find(node, filter_=None, stop=None, maxlevel=None): """Identical to :any:`search.find` but cached.""" return search.find(node, filter_=filter_, stop=stop, maxlevel=maxlevel) @_cache(CACHE_SIZE) def find_by_attr(node, value, name="name", maxlevel=None): """Identical to :any:`search.find_by_attr` but cached.""" return search.find_by_attr(node, value, name=name, maxlevel=maxlevel) anytree-2.13.0/src/anytree/config.py000066400000000000000000000002421500063244000173110ustar00rootroot00000000000000"""Central Configuration.""" import os # Global Option which enables all internal assertions. ASSERTIONS = bool(int(os.environ.get("ANYTREE_ASSERTIONS", "0"))) anytree-2.13.0/src/anytree/dotexport.py000066400000000000000000000006761500063244000201070ustar00rootroot00000000000000import warnings from anytree.exporter.dotexporter import DotExporter class RenderTreeGraph(DotExporter): def __init__(self, *args, **kwargs): """Legacy. Use :any:`anytree.exporter.DotExporter` instead.""" warnings.warn( ("anytree.RenderTreeGraph has moved. Use anytree.exporter.DotExporter instead"), DeprecationWarning, stacklevel=2, ) super().__init__(*args, **kwargs) anytree-2.13.0/src/anytree/exporter/000077500000000000000000000000001500063244000173445ustar00rootroot00000000000000anytree-2.13.0/src/anytree/exporter/__init__.py000066400000000000000000000004761500063244000214640ustar00rootroot00000000000000"""Exporter.""" from .dictexporter import DictExporter from .dotexporter import DotExporter, UniqueDotExporter from .jsonexporter import JsonExporter from .mermaidexporter import MermaidExporter __all__ = [ "DictExporter", "DotExporter", "JsonExporter", "MermaidExporter", "UniqueDotExporter", ] anytree-2.13.0/src/anytree/exporter/dictexporter.py000066400000000000000000000057761500063244000224510ustar00rootroot00000000000000class DictExporter: """ Tree to dictionary exporter. Every node is converted to a dictionary with all instance attributes as key-value pairs. Child nodes are exported to the children attribute. A list of dictionaries. Keyword Args: dictcls: class used as dictionary. :any:`dict` by default. attriter: attribute iterator for sorting and/or filtering. childiter: child iterator for sorting and/or filtering. maxlevel (int): Limit export to this number of levels. >>> from pprint import pprint # just for nice printing >>> from anytree import AnyNode >>> from anytree.exporter import DictExporter >>> root = AnyNode(a="root") >>> s0 = AnyNode(a="sub0", parent=root) >>> s0a = AnyNode(a="sub0A", b="foo", parent=s0) >>> s0b = AnyNode(a="sub0B", parent=s0) >>> s1 = AnyNode(a="sub1", parent=root) >>> exporter = DictExporter() >>> pprint(exporter.export(root)) {'a': 'root', 'children': [{'a': 'sub0', 'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]}, {'a': 'sub1'}]} The attribute iterator `attriter` may be used for filtering too. For example, just dump attributes named `a`: >>> exporter = DictExporter(attriter=lambda attrs: [(k, v) for k, v in attrs if k == "a"]) >>> pprint(exporter.export(root)) {'a': 'root', 'children': [{'a': 'sub0', 'children': [{'a': 'sub0A'}, {'a': 'sub0B'}]}, {'a': 'sub1'}]} The child iterator `childiter` can be used for sorting and filtering likewise: >>> exporter = DictExporter(childiter=lambda children: [child for child in children if "0" in child.a]) >>> pprint(exporter.export(root)) {'a': 'root', 'children': [{'a': 'sub0', 'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]}]} """ def __init__(self, dictcls=dict, attriter=None, childiter=list, maxlevel=None): self.dictcls = dictcls self.attriter = attriter self.childiter = childiter self.maxlevel = maxlevel def export(self, node): """Export tree starting at `node`.""" attriter = self.attriter or (lambda attr_values: attr_values) return self.__export(node, self.dictcls, attriter, self.childiter) def __export(self, node, dictcls, attriter, childiter, level=1): attr_values = attriter(self._iter_attr_values(node)) data = dictcls(attr_values) maxlevel = self.maxlevel if maxlevel is None or level < maxlevel: children = [ self.__export(child, dictcls, attriter, childiter, level=level + 1) for child in childiter(node.children) ] if children: data["children"] = children return data @staticmethod def _iter_attr_values(node): # pylint: disable=C0103 for k, v in node.__dict__.items(): if k in ("_NodeMixin__children", "_NodeMixin__parent"): continue yield k, v anytree-2.13.0/src/anytree/exporter/dotexporter.py000066400000000000000000000353431500063244000223050ustar00rootroot00000000000000import codecs import itertools import logging import re from os import path, remove from subprocess import check_call from tempfile import NamedTemporaryFile from anytree import PreOrderIter _RE_ESC = re.compile(r'["\\]') class DotExporter: """ Dot Language Exporter. Args: node (Node): start node. Keyword Args: graph: DOT graph type. name: DOT graph name. options: list of options added to the graph. indent (int): number of spaces for indent. nodenamefunc: Function to extract node name from `node` object. The function shall accept one `node` object as argument and return the name of it. nodeattrfunc: Function to decorate a node with attributes. The function shall accept one `node` object as argument and return the attributes. edgeattrfunc: Function to decorate a edge with attributes. The function shall accept two `node` objects as argument. The first the node and the second the child and return the attributes. edgetypefunc: Function to which gives the edge type. The function shall accept two `node` objects as argument. The first the node and the second the child and return the edge (i.e. '->'). filter_: Function to filter nodes to include in export. The function shall accept one `node` object as argument and return True if it should be included, or False if it should not be included. stop: stop iteration at `node` if `stop` function returns `True` for `node`. maxlevel (int): Limit export to this number of levels. >>> from anytree import Node >>> root = Node("root") >>> s0 = Node("sub0", parent=root, edge=2) >>> s0b = Node("sub0B", parent=s0, foo=4, edge=109) >>> s0a = Node("sub0A", parent=s0, edge="") >>> s1 = Node("sub1", parent=root, edge="") >>> s1a = Node("sub1A", parent=s1, edge=7) >>> s1b = Node("sub1B", parent=s1, edge=8) >>> s1c = Node("sub1C", parent=s1, edge=22) >>> s1ca = Node("sub1Ca", parent=s1c, edge=42) .. note:: If the node names are not unique, see :any:`UniqueDotExporter`. A directed graph: >>> from anytree.exporter import DotExporter >>> for line in DotExporter(root): ... print(line) digraph tree { "root"; "sub0"; "sub0B"; "sub0A"; "sub1"; "sub1A"; "sub1B"; "sub1C"; "sub1Ca"; "root" -> "sub0"; "root" -> "sub1"; "sub0" -> "sub0B"; "sub0" -> "sub0A"; "sub1" -> "sub1A"; "sub1" -> "sub1B"; "sub1" -> "sub1C"; "sub1C" -> "sub1Ca"; } The resulting graph: .. image:: ../static/dotexporter0.png An undirected graph: >>> def nodenamefunc(node): ... return '%s:%s' % (node.name, node.depth) >>> def edgeattrfunc(node, child): ... return 'label="%s:%s"' % (node.name, child.name) >>> def edgetypefunc(node, child): ... return '--' >>> from anytree.exporter import DotExporter >>> for line in DotExporter(root, graph="graph", ... nodenamefunc=nodenamefunc, ... nodeattrfunc=lambda node: "shape=box", ... edgeattrfunc=edgeattrfunc, ... edgetypefunc=edgetypefunc): ... print(line) graph tree { "root:0" [shape=box]; "sub0:1" [shape=box]; "sub0B:2" [shape=box]; "sub0A:2" [shape=box]; "sub1:1" [shape=box]; "sub1A:2" [shape=box]; "sub1B:2" [shape=box]; "sub1C:2" [shape=box]; "sub1Ca:3" [shape=box]; "root:0" -- "sub0:1" [label="root:sub0"]; "root:0" -- "sub1:1" [label="root:sub1"]; "sub0:1" -- "sub0B:2" [label="sub0:sub0B"]; "sub0:1" -- "sub0A:2" [label="sub0:sub0A"]; "sub1:1" -- "sub1A:2" [label="sub1:sub1A"]; "sub1:1" -- "sub1B:2" [label="sub1:sub1B"]; "sub1:1" -- "sub1C:2" [label="sub1:sub1C"]; "sub1C:2" -- "sub1Ca:3" [label="sub1C:sub1Ca"]; } The resulting graph: .. image:: ../static/dotexporter1.png To export custom node implementations or :any:`AnyNode`, please provide a proper `nodenamefunc`: >>> from anytree import AnyNode >>> root = AnyNode(id="root") >>> s0 = AnyNode(id="sub0", parent=root) >>> s0b = AnyNode(id="s0b", parent=s0) >>> s0a = AnyNode(id="s0a", parent=s0) >>> from anytree.exporter import DotExporter >>> for line in DotExporter(root, nodenamefunc=lambda n: n.id): ... print(line) digraph tree { "root"; "sub0"; "s0b"; "s0a"; "root" -> "sub0"; "sub0" -> "s0b"; "sub0" -> "s0a"; } """ def __init__( self, node, graph="digraph", name="tree", options=None, indent=4, nodenamefunc=None, nodeattrfunc=None, edgeattrfunc=None, edgetypefunc=None, filter_=None, maxlevel=None, stop=None, ): self.node = node self.graph = graph self.name = name self.options = options self.indent = indent self.nodenamefunc = nodenamefunc self.nodeattrfunc = nodeattrfunc self.edgeattrfunc = edgeattrfunc self.edgetypefunc = edgetypefunc self.filter_ = filter_ self.maxlevel = maxlevel self.stop = stop def __iter__(self): # prepare indent = " " * self.indent nodenamefunc = self.nodenamefunc or self._default_nodenamefunc nodeattrfunc = self.nodeattrfunc or self._default_nodeattrfunc edgeattrfunc = self.edgeattrfunc or self._default_edgeattrfunc edgetypefunc = self.edgetypefunc or self._default_edgetypefunc filter_ = self.filter_ or self._default_filter return self.__iter(indent, nodenamefunc, nodeattrfunc, edgeattrfunc, edgetypefunc, filter_) @staticmethod def _default_nodenamefunc(node): return node.name @staticmethod def _default_nodeattrfunc(node): # pylint: disable=unused-argument return None @staticmethod def _default_edgeattrfunc(node, child): # pylint: disable=unused-argument return None @staticmethod def _default_edgetypefunc(node, child): # pylint: disable=unused-argument return "->" @staticmethod def _default_filter(node): # pylint: disable=unused-argument return True def __iter(self, indent, nodenamefunc, nodeattrfunc, edgeattrfunc, edgetypefunc, filter_): yield f"{self.graph} {self.name} {{" yield from self.__iter_options(indent) yield from self.__iter_nodes(indent, nodenamefunc, nodeattrfunc, filter_) yield from self.__iter_edges(indent, nodenamefunc, edgeattrfunc, edgetypefunc, filter_) yield "}" def __iter_options(self, indent): options = self.options if options: for option in options: yield f"{indent}{option}" def __iter_nodes(self, indent, nodenamefunc, nodeattrfunc, filter_): for node in PreOrderIter(self.node, filter_=filter_, stop=self.stop, maxlevel=self.maxlevel): nodename = nodenamefunc(node) nodeattr = nodeattrfunc(node) nodeattr = f" [{nodeattr}]" if nodeattr is not None else "" yield f'{indent}"{DotExporter.esc(nodename)}"{nodeattr};' def __iter_edges(self, indent, nodenamefunc, edgeattrfunc, edgetypefunc, filter_): maxlevel = self.maxlevel - 1 if self.maxlevel else None for node in PreOrderIter(self.node, filter_=filter_, stop=self.stop, maxlevel=maxlevel): nodename = nodenamefunc(node) for child in node.children: if not filter_(child): continue childname = nodenamefunc(child) edgeattr = edgeattrfunc(node, child) edgetype = edgetypefunc(node, child) edgeattr = f" [{edgeattr}]" if edgeattr is not None else "" yield f'{indent}"{DotExporter.esc(nodename)}" {edgetype} "{DotExporter.esc(childname)}"{edgeattr};' def to_dotfile(self, filename): """ Write graph to `filename`. >>> from anytree import Node >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> s1a = Node("sub1A", parent=s1) >>> s1b = Node("sub1B", parent=s1) >>> s1c = Node("sub1C", parent=s1) >>> s1ca = Node("sub1Ca", parent=s1c) >>> from anytree.exporter import DotExporter >>> DotExporter(root).to_dotfile("tree.dot") The generated file should be handed over to the `dot` tool from the http://www.graphviz.org/ package:: $ dot tree.dot -T png -o tree.png """ with codecs.open(filename, "w", "utf-8") as file: for line in self: file.write(f"{line}\n") def to_picture(self, filename): """ Write graph to a temporary file and invoke `dot`. The output file type is automatically detected from the file suffix. *`graphviz` needs to be installed, before usage of this method.* """ fileformat = path.splitext(filename)[1][1:] with NamedTemporaryFile("wb", delete=False) as dotfile: dotfilename = dotfile.name for line in self: dotfile.write((f"{line}\n").encode()) dotfile.flush() cmd = ["dot", dotfilename, "-T", fileformat, "-o", filename] check_call(cmd) try: remove(dotfilename) # pylint: disable=broad-exception-caught except Exception: # pragma: no cover logging.getLogger(__name__).warning("Could not remove temporary file %s", dotfilename) @staticmethod def esc(value): """Escape Strings.""" return _RE_ESC.sub(lambda m: rf"\{m.group(0)}", str(value)) class UniqueDotExporter(DotExporter): """ Unique Dot Language Exporter. Handle trees with random or conflicting node names gracefully. Args: node (Node): start node. Keyword Args: graph: DOT graph type. name: DOT graph name. options: list of options added to the graph. indent (int): number of spaces for indent. nodenamefunc: Function to extract node name from `node` object. The function shall accept one `node` object as argument and return the name of it. nodeattrfunc: Function to decorate a node with attributes. The function shall accept one `node` object as argument and return the attributes. edgeattrfunc: Function to decorate a edge with attributes. The function shall accept two `node` objects as argument. The first the node and the second the child and return the attributes. edgetypefunc: Function to which gives the edge type. The function shall accept two `node` objects as argument. The first the node and the second the child and return the edge (i.e. '->'). filter_: Function to filter nodes to include in export. The function shall accept one `node` object as argument and return True if it should be included, or False if it should not be included. stop: stop iteration at `node` if `stop` function returns `True` for `node`. maxlevel (int): Limit export to this number of levels. >>> from anytree import Node >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("s0", parent=s0) >>> s0a = Node("s0", parent=s0) >>> s1 = Node("sub1", parent=root) >>> s1a = Node("s1", parent=s1) >>> s1b = Node("s1", parent=s1) >>> s1c = Node("s1", parent=s1) >>> s1ca = Node("sub1Ca", parent=s1c) >>> from anytree.exporter import UniqueDotExporter >>> for line in UniqueDotExporter(root): ... print(line) digraph tree { "0x0" [label="root"]; "0x1" [label="sub0"]; "0x2" [label="s0"]; "0x3" [label="s0"]; "0x4" [label="sub1"]; "0x5" [label="s1"]; "0x6" [label="s1"]; "0x7" [label="s1"]; "0x8" [label="sub1Ca"]; "0x0" -> "0x1"; "0x0" -> "0x4"; "0x1" -> "0x2"; "0x1" -> "0x3"; "0x4" -> "0x5"; "0x4" -> "0x6"; "0x4" -> "0x7"; "0x7" -> "0x8"; } The resulting graph: .. image:: ../static/uniquedotexporter2.png To export custom node implementations or :any:`AnyNode`, please provide a proper `nodeattrfunc`: >>> from anytree import AnyNode >>> root = AnyNode(id="root") >>> s0 = AnyNode(id="sub0", parent=root) >>> s0b = AnyNode(id="s0", parent=s0) >>> s0a = AnyNode(id="s0", parent=s0) >>> from anytree.exporter import UniqueDotExporter >>> for line in UniqueDotExporter(root, nodeattrfunc=lambda n: 'label="%s"' % (n.id)): ... print(line) digraph tree { "0x0" [label="root"]; "0x1" [label="sub0"]; "0x2" [label="s0"]; "0x3" [label="s0"]; "0x0" -> "0x1"; "0x1" -> "0x2"; "0x1" -> "0x3"; } """ def __init__( self, node, graph="digraph", name="tree", options=None, indent=4, nodenamefunc=None, nodeattrfunc=None, edgeattrfunc=None, edgetypefunc=None, filter_=None, stop=None, maxlevel=None, ): super().__init__( node, graph=graph, name=name, options=options, indent=indent, nodenamefunc=nodenamefunc, nodeattrfunc=nodeattrfunc, edgeattrfunc=edgeattrfunc, edgetypefunc=edgetypefunc, filter_=filter_, stop=stop, maxlevel=maxlevel, ) self.__node_ids = {} self.__node_counter = itertools.count() # pylint: disable=arguments-differ def _default_nodenamefunc(self, node): node_id = id(node) try: num = self.__node_ids[node_id] except KeyError: num = self.__node_ids[node_id] = next(self.__node_counter) return hex(num) @staticmethod def _default_nodeattrfunc(node): return f'label="{node.name}"' anytree-2.13.0/src/anytree/exporter/jsonexporter.py000066400000000000000000000042321500063244000224610ustar00rootroot00000000000000import json from .dictexporter import DictExporter class JsonExporter: """ Tree to JSON exporter. The tree is converted to a dictionary via `dictexporter` and exported to JSON. Keyword Arguments: dictexporter: Dictionary Exporter used (see :any:`DictExporter`). maxlevel (int): Limit export to this number of levels. kwargs: All other arguments are passed to :any:`json.dump`/:any:`json.dumps`. See documentation for reference. >>> from anytree import AnyNode >>> from anytree.exporter import JsonExporter >>> root = AnyNode(a="root") >>> s0 = AnyNode(a="sub0", parent=root) >>> s0a = AnyNode(a="sub0A", b="foo", parent=s0) >>> s0b = AnyNode(a="sub0B", parent=s0) >>> s1 = AnyNode(a="sub1", parent=root) >>> exporter = JsonExporter(indent=2, sort_keys=True) >>> print(exporter.export(root)) { "a": "root", "children": [ { "a": "sub0", "children": [ { "a": "sub0A", "b": "foo" }, { "a": "sub0B" } ] }, { "a": "sub1" } ] } .. note:: Whenever the json output does not meet your expectations, see the :any:`json` documentation. For instance, if you have unicode/ascii issues, please try `JsonExporter(..., ensure_ascii=False)`. """ def __init__(self, dictexporter=None, maxlevel=None, **kwargs): self.dictexporter = dictexporter self.maxlevel = maxlevel self.kwargs = kwargs def _export(self, node): dictexporter = self.dictexporter or DictExporter() if self.maxlevel is not None: dictexporter.maxlevel = self.maxlevel return dictexporter.export(node) def export(self, node): """Return JSON for tree starting at `node`.""" data = self._export(node) return json.dumps(data, **self.kwargs) def write(self, node, filehandle): """Write JSON to `filehandle` starting at `node`.""" data = self._export(node) return json.dump(data, filehandle, **self.kwargs) anytree-2.13.0/src/anytree/exporter/mermaidexporter.py000066400000000000000000000161071500063244000231320ustar00rootroot00000000000000import codecs import itertools import re from anytree import PreOrderIter _RE_ESC = re.compile(r'["\\]') class MermaidExporter: """ Mermaid Exporter. Args: node (Node): start node. Keyword Args: graph: Mermaid graph type. name: Mermaid graph name. options: list of options added to the graph. indent (int): number of spaces for indent. nodenamefunc: Function to extract node name from `node` object. The function shall accept one `node` object as argument and return the name of it. Returns a unique identifier by default. nodefunc: Function to decorate a node with attributes. The function shall accept one `node` object as argument and return the attributes. Returns ``[{node.name}]`` and creates therefore a rectangular node by default. edgefunc: Function to decorate a edge with attributes. The function shall accept two `node` objects as argument. The first the node and the second the child and return edge. Returns ``-->`` by default. filter_: Function to filter nodes to include in export. The function shall accept one `node` object as argument and return True if it should be included, or False if it should not be included. stop: stop iteration at `node` if `stop` function returns `True` for `node`. maxlevel (int): Limit export to this number of levels. >>> from anytree import Node >>> root = Node("root") >>> s0 = Node("sub0", parent=root, edge=2) >>> s0b = Node("sub0B", parent=s0, foo=4, edge=109) >>> s0a = Node("sub0A", parent=s0, edge="") >>> s1 = Node("sub1", parent=root, edge="") >>> s1a = Node("sub1A", parent=s1, edge=7) >>> s1b = Node("sub1B", parent=s1, edge=8) >>> s1c = Node("sub1C", parent=s1, edge=22) >>> s1ca = Node("sub1Ca", parent=s1c, edge=42) A top-down graph: >>> from anytree.exporter import MermaidExporter >>> for line in MermaidExporter(root): ... print(line) graph TD N0["root"] N1["sub0"] N2["sub0B"] N3["sub0A"] N4["sub1"] N5["sub1A"] N6["sub1B"] N7["sub1C"] N8["sub1Ca"] N0-->N1 N0-->N4 N1-->N2 N1-->N3 N4-->N5 N4-->N6 N4-->N7 N7-->N8 A customized graph with round boxes and named arrows: >>> def nodefunc(node): ... return '("%s")' % (node.name) >>> def edgefunc(node, child): ... return f"--{child.edge}-->" >>> options = [ ... "%% just an example comment", ... "%% could be an option too", ... ] >>> for line in MermaidExporter(root, options=options, nodefunc=nodefunc, edgefunc=edgefunc): ... print(line) graph TD %% just an example comment %% could be an option too N0("root") N1("sub0") N2("sub0B") N3("sub0A") N4("sub1") N5("sub1A") N6("sub1B") N7("sub1C") N8("sub1Ca") N0--2-->N1 N0---->N4 N1--109-->N2 N1---->N3 N4--7-->N5 N4--8-->N6 N4--22-->N7 N7--42-->N8 """ def __init__( self, node, graph="graph", name="TD", options=None, indent=0, nodenamefunc=None, nodefunc=None, edgefunc=None, filter_=None, stop=None, maxlevel=None, ): self.node = node self.graph = graph self.name = name self.options = options self.indent = indent self.nodenamefunc = nodenamefunc self.nodefunc = nodefunc self.edgefunc = edgefunc self.filter_ = filter_ self.stop = stop self.maxlevel = maxlevel self.__node_ids = {} self.__node_counter = itertools.count() def __iter__(self): # prepare indent = " " * self.indent nodenamefunc = self.nodenamefunc or self._default_nodenamefunc nodefunc = self.nodefunc or self._default_nodefunc edgefunc = self.edgefunc or self._default_edgefunc filter_ = self.filter_ or (lambda node: True) stop = self.stop or (lambda node: False) return self.__iter(indent, nodenamefunc, nodefunc, edgefunc, filter_, stop) # pylint: disable=arguments-differ def _default_nodenamefunc(self, node): node_id = id(node) try: num = self.__node_ids[node_id] except KeyError: num = self.__node_ids[node_id] = next(self.__node_counter) return f"N{num}" @staticmethod def _default_nodefunc(node): # pylint: disable=W0613 return f'["{MermaidExporter.esc(node.name)}"]' @staticmethod def _default_edgefunc(node, child): # pylint: disable=W0613 return "-->" def __iter(self, indent, nodenamefunc, nodefunc, edgefunc, filter_, stop): yield f"{self.graph} {self.name}" yield from self.__iter_options(indent) yield from self.__iter_nodes(indent, nodenamefunc, nodefunc, filter_, stop) yield from self.__iter_edges(indent, nodenamefunc, edgefunc, filter_, stop) def __iter_options(self, indent): options = self.options if options: for option in options: yield f"{indent}{option}" def __iter_nodes(self, indent, nodenamefunc, nodefunc, filter_, stop): for node in PreOrderIter(self.node, filter_=filter_, stop=stop, maxlevel=self.maxlevel): nodename = nodenamefunc(node) yield f"{indent}{nodename}{nodefunc(node)}" def __iter_edges(self, indent, nodenamefunc, edgefunc, filter_, stop): maxlevel = self.maxlevel - 1 if self.maxlevel else None for node in PreOrderIter(self.node, filter_=filter_, stop=stop, maxlevel=maxlevel): nodename = nodenamefunc(node) for child in node.children: if filter_(child) and not stop(child): childname = nodenamefunc(child) edge = edgefunc(node, child) yield f"{indent}{nodename}{edge}{childname}" def to_file(self, filename): """ Write graph to `filename`. >>> from anytree import Node >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> s1a = Node("sub1A", parent=s1) >>> s1b = Node("sub1B", parent=s1) >>> s1c = Node("sub1C", parent=s1) >>> s1ca = Node("sub1Ca", parent=s1c) >>> from anytree.exporter import MermaidExporter >>> MermaidExporter(root).to_file("tree.md") """ with codecs.open(filename, "w", "utf-8") as file: file.write("```mermaid\n") for line in self: file.write(f"{line}\n") file.write("```") @staticmethod def esc(value): """Escape Strings.""" return _RE_ESC.sub(lambda m: rf"\{m.group(0)}", str(value)) anytree-2.13.0/src/anytree/importer/000077500000000000000000000000001500063244000173355ustar00rootroot00000000000000anytree-2.13.0/src/anytree/importer/__init__.py000066400000000000000000000002261500063244000214460ustar00rootroot00000000000000"""Importer.""" from .dictimporter import DictImporter from .jsonimporter import JsonImporter __all__ = [ "DictImporter", "JsonImporter", ] anytree-2.13.0/src/anytree/importer/dictimporter.py000066400000000000000000000027411500063244000224200ustar00rootroot00000000000000from anytree import AnyNode from anytree.config import ASSERTIONS class DictImporter: """ Import Tree from dictionary. Every dictionary is converted to an instance of `nodecls`. The dictionaries listed in the children attribute are converted likewise and added as children. Keyword Args: nodecls: class used for nodes. >>> from anytree.importer import DictImporter >>> from anytree import RenderTree >>> importer = DictImporter() >>> data = { ... 'a': 'root', ... 'children': [{'a': 'sub0', ... 'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]}, ... {'a': 'sub1'}]} >>> root = importer.import_(data) >>> print(RenderTree(root)) AnyNode(a='root') ├── AnyNode(a='sub0') │ ├── AnyNode(a='sub0A', b='foo') │ └── AnyNode(a='sub0B') └── AnyNode(a='sub1') """ def __init__(self, nodecls=AnyNode): self.nodecls = nodecls def import_(self, data): """Import tree from `data`.""" return self.__import(data) def __import(self, data, parent=None): if ASSERTIONS: # pragma: no branch assert isinstance(data, dict) assert "parent" not in data attrs = dict(data) children = attrs.pop("children", []) node = self.nodecls(parent=parent, **attrs) for child in children: self.__import(child, parent=node) return node anytree-2.13.0/src/anytree/importer/jsonimporter.py000066400000000000000000000033311500063244000224420ustar00rootroot00000000000000import json from .dictimporter import DictImporter class JsonImporter: """ Import Tree from JSON. The JSON is read and converted to a dictionary via `dictimporter`. Keyword Arguments: dictimporter: Dictionary Importer used (see :any:`DictImporter`). kwargs: All other arguments are passed to :any:`json.load`/:any:`json.loads`. See documentation for reference. >>> from anytree.importer import JsonImporter >>> from anytree import RenderTree >>> importer = JsonImporter() >>> data = ''' ... { ... "a": "root", ... "children": [ ... { ... "a": "sub0", ... "children": [ ... { ... "a": "sub0A", ... "b": "foo" ... }, ... { ... "a": "sub0B" ... } ... ] ... }, ... { ... "a": "sub1" ... } ... ] ... }''' >>> root = importer.import_(data) >>> print(RenderTree(root)) AnyNode(a='root') ├── AnyNode(a='sub0') │ ├── AnyNode(a='sub0A', b='foo') │ └── AnyNode(a='sub0B') └── AnyNode(a='sub1') """ def __init__(self, dictimporter=None, **kwargs): self.dictimporter = dictimporter self.kwargs = kwargs def __import(self, data): dictimporter = self.dictimporter or DictImporter() return dictimporter.import_(data) def import_(self, data): """Read JSON from `data`.""" return self.__import(json.loads(data, **self.kwargs)) def read(self, filehandle): """Read JSON from `filehandle`.""" return self.__import(json.load(filehandle, **self.kwargs)) anytree-2.13.0/src/anytree/iterators/000077500000000000000000000000001500063244000175105ustar00rootroot00000000000000anytree-2.13.0/src/anytree/iterators/__init__.py000066400000000000000000000015621500063244000216250ustar00rootroot00000000000000""" Tree Iteration. * :any:`PreOrderIter`: iterate over tree using pre-order strategy (self, children) * :any:`PostOrderIter`: iterate over tree using post-order strategy (children, self) * :any:`LevelOrderIter`: iterate over tree using level-order strategy * :any:`LevelOrderGroupIter`: iterate over tree using level-order strategy returning group for every level * :any:`ZigZagGroupIter`: iterate over tree using level-order strategy returning group for every level """ from .abstractiter import AbstractIter from .levelordergroupiter import LevelOrderGroupIter from .levelorderiter import LevelOrderIter from .postorderiter import PostOrderIter from .preorderiter import PreOrderIter from .zigzaggroupiter import ZigZagGroupIter __all__ = [ "AbstractIter", "LevelOrderGroupIter", "LevelOrderIter", "PostOrderIter", "PreOrderIter", "ZigZagGroupIter", ] anytree-2.13.0/src/anytree/iterators/abstractiter.py000066400000000000000000000032631500063244000225550ustar00rootroot00000000000000class AbstractIter: # pylint: disable=R0205 """ Iterate over tree starting at `node`. Base class for all iterators. Keyword Args: filter_: function called with every `node` as argument, `node` is returned if `True`. stop: stop iteration at `node` if `stop` function returns `True` for `node`. maxlevel (int): maximum descending in the node hierarchy. """ def __init__(self, node, filter_=None, stop=None, maxlevel=None): self.node = node self.filter_ = filter_ self.stop = stop self.maxlevel = maxlevel self.__iter = None def __init(self): node = self.node maxlevel = self.maxlevel filter_ = self.filter_ or AbstractIter.__default_filter stop = self.stop or AbstractIter.__default_stop children = [] if AbstractIter._abort_at_level(1, maxlevel) else AbstractIter._get_children([node], stop) return self._iter(children, filter_, stop, maxlevel) @staticmethod def __default_filter(node): # pylint: disable=W0613 return True @staticmethod def __default_stop(node): # pylint: disable=W0613 return False def __iter__(self): return self def __next__(self): if self.__iter is None: self.__iter = self.__init() return next(self.__iter) @staticmethod def _iter(children, filter_, stop, maxlevel): raise NotImplementedError @staticmethod def _abort_at_level(level, maxlevel): return maxlevel is not None and level > maxlevel @staticmethod def _get_children(children, stop): return [child for child in children if not stop(child)] anytree-2.13.0/src/anytree/iterators/levelordergroupiter.py000066400000000000000000000042341500063244000241710ustar00rootroot00000000000000from .abstractiter import AbstractIter class LevelOrderGroupIter(AbstractIter): """ Iterate over tree applying level-order strategy with grouping starting at `node`. Return a tuple of nodes for each level. The first tuple contains the nodes at level 0 (always `node`). The second tuple contains the nodes at level 1 (children of `node`). The next level contains the children of the children, and so on. >>> from anytree import Node, RenderTree, AsciiStyle, LevelOrderGroupIter >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> [[node.name for node in children] for children in LevelOrderGroupIter(f)] [['f'], ['b', 'g'], ['a', 'd', 'i'], ['c', 'e', 'h']] >>> [[node.name for node in children] for children in LevelOrderGroupIter(f, maxlevel=3)] [['f'], ['b', 'g'], ['a', 'd', 'i']] >>> [[node.name for node in children] ... for children in LevelOrderGroupIter(f, filter_=lambda n: n.name not in ('e', 'g'))] [['f'], ['b'], ['a', 'd', 'i'], ['c', 'h']] >>> [[node.name for node in children] ... for children in LevelOrderGroupIter(f, stop=lambda n: n.name == 'd')] [['f'], ['b', 'g'], ['a', 'i'], ['h']] """ @staticmethod def _iter(children, filter_, stop, maxlevel): level = 1 while children: yield tuple(child for child in children if filter_(child)) level += 1 if AbstractIter._abort_at_level(level, maxlevel): break children = LevelOrderGroupIter._get_grandchildren(children, stop) @staticmethod def _get_grandchildren(children, stop): next_children = [] for child in children: next_children = next_children + AbstractIter._get_children(child.children, stop) return next_children anytree-2.13.0/src/anytree/iterators/levelorderiter.py000066400000000000000000000033141500063244000231120ustar00rootroot00000000000000from .abstractiter import AbstractIter class LevelOrderIter(AbstractIter): """ Iterate over tree applying level-order strategy starting at `node`. >>> from anytree import Node, RenderTree, AsciiStyle, LevelOrderIter >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> [node.name for node in LevelOrderIter(f)] ['f', 'b', 'g', 'a', 'd', 'i', 'c', 'e', 'h'] >>> [node.name for node in LevelOrderIter(f, maxlevel=3)] ['f', 'b', 'g', 'a', 'd', 'i'] >>> [node.name for node in LevelOrderIter(f, filter_=lambda n: n.name not in ('e', 'g'))] ['f', 'b', 'a', 'd', 'i', 'c', 'h'] >>> [node.name for node in LevelOrderIter(f, stop=lambda n: n.name == 'd')] ['f', 'b', 'g', 'a', 'i', 'h'] """ @staticmethod def _iter(children, filter_, stop, maxlevel): level = 1 while children: next_children = [] level += 1 if AbstractIter._abort_at_level(level, maxlevel): for child in children: if filter_(child): yield child else: for child in children: if filter_(child): yield child next_children += AbstractIter._get_children(child.children, stop) children = next_children anytree-2.13.0/src/anytree/iterators/postorderiter.py000066400000000000000000000032451500063244000227730ustar00rootroot00000000000000from .abstractiter import AbstractIter class PostOrderIter(AbstractIter): """ Iterate over tree applying post-order strategy starting at `node`. >>> from anytree import Node, RenderTree, AsciiStyle, PostOrderIter >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> [node.name for node in PostOrderIter(f)] ['a', 'c', 'e', 'd', 'b', 'h', 'i', 'g', 'f'] >>> [node.name for node in PostOrderIter(f, maxlevel=3)] ['a', 'd', 'b', 'i', 'g', 'f'] >>> [node.name for node in PostOrderIter(f, filter_=lambda n: n.name not in ('e', 'g'))] ['a', 'c', 'd', 'b', 'h', 'i', 'f'] >>> [node.name for node in PostOrderIter(f, stop=lambda n: n.name == 'd')] ['a', 'b', 'h', 'i', 'g', 'f'] """ @staticmethod def _iter(children, filter_, stop, maxlevel): return PostOrderIter.__next(children, 1, filter_, stop, maxlevel) @staticmethod def __next(children, level, filter_, stop, maxlevel): if not AbstractIter._abort_at_level(level, maxlevel): for child in children: grandchildren = AbstractIter._get_children(child.children, stop) yield from PostOrderIter.__next(grandchildren, level + 1, filter_, stop, maxlevel) if filter_(child): yield child anytree-2.13.0/src/anytree/iterators/preorderiter.py000066400000000000000000000032331500063244000225710ustar00rootroot00000000000000from .abstractiter import AbstractIter class PreOrderIter(AbstractIter): """ Iterate over tree applying pre-order strategy starting at `node`. Start at root and go-down until reaching a leaf node. Step upwards then, and search for the next leafs. >>> from anytree import Node, RenderTree, AsciiStyle, PreOrderIter >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> [node.name for node in PreOrderIter(f)] ['f', 'b', 'a', 'd', 'c', 'e', 'g', 'i', 'h'] >>> [node.name for node in PreOrderIter(f, maxlevel=3)] ['f', 'b', 'a', 'd', 'g', 'i'] >>> [node.name for node in PreOrderIter(f, filter_=lambda n: n.name not in ('e', 'g'))] ['f', 'b', 'a', 'd', 'c', 'i', 'h'] >>> [node.name for node in PreOrderIter(f, stop=lambda n: n.name == 'd')] ['f', 'b', 'a', 'g', 'i', 'h'] """ @staticmethod def _iter(children, filter_, stop, maxlevel): for child_ in children: if stop(child_): continue if filter_(child_): yield child_ if not AbstractIter._abort_at_level(2, maxlevel): descendantmaxlevel = maxlevel - 1 if maxlevel else None yield from PreOrderIter._iter(child_.children, filter_, stop, descendantmaxlevel) anytree-2.13.0/src/anytree/iterators/zigzaggroupiter.py000066400000000000000000000041551500063244000233230ustar00rootroot00000000000000from anytree.config import ASSERTIONS from .abstractiter import AbstractIter from .levelordergroupiter import LevelOrderGroupIter class ZigZagGroupIter(AbstractIter): """ Iterate over tree applying Zig-Zag strategy with grouping starting at `node`. Return a tuple of nodes for each level. The first tuple contains the nodes at level 0 (always `node`). The second tuple contains the nodes at level 1 (children of `node`) in reversed order. The next level contains the children of the children in forward order, and so on. >>> from anytree import Node, RenderTree, AsciiStyle, ZigZagGroupIter >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> [[node.name for node in children] for children in ZigZagGroupIter(f)] [['f'], ['g', 'b'], ['a', 'd', 'i'], ['h', 'e', 'c']] >>> [[node.name for node in children] for children in ZigZagGroupIter(f, maxlevel=3)] [['f'], ['g', 'b'], ['a', 'd', 'i']] >>> [[node.name for node in children] ... for children in ZigZagGroupIter(f, filter_=lambda n: n.name not in ('e', 'g'))] [['f'], ['b'], ['a', 'd', 'i'], ['h', 'c']] >>> [[node.name for node in children] ... for children in ZigZagGroupIter(f, stop=lambda n: n.name == 'd')] [['f'], ['g', 'b'], ['a', 'i'], ['h']] """ @staticmethod def _iter(children, filter_, stop, maxlevel): if children: if ASSERTIONS: # pragma: no branch assert len(children) == 1 _iter = LevelOrderGroupIter(children[0], filter_, stop, maxlevel) while True: try: yield next(_iter) yield tuple(reversed(next(_iter))) except StopIteration: break anytree-2.13.0/src/anytree/node/000077500000000000000000000000001500063244000164215ustar00rootroot00000000000000anytree-2.13.0/src/anytree/node/__init__.py000066400000000000000000000015721500063244000205370ustar00rootroot00000000000000""" Node Classes. * :any:`AnyNode`: a generic tree node with any number of attributes. * :any:`Node`: a simple tree node with at least a name attribute and any number of additional attributes. * :any:`NodeMixin`: extends any python class to a tree node. * :any:`SymlinkNode`: Tree node which references to another tree node. * :any:`SymlinkNodeMixin`: extends any Python class to a symbolic link to a tree node. * :any:`LightNodeMixin`: A :any:`NodeMixin` using slots. """ from .anynode import AnyNode from .exceptions import LoopError, TreeError from .lightnodemixin import LightNodeMixin from .node import Node from .nodemixin import NodeMixin from .symlinknode import SymlinkNode from .symlinknodemixin import SymlinkNodeMixin __all__ = [ "AnyNode", "LightNodeMixin", "LoopError", "Node", "NodeMixin", "SymlinkNode", "SymlinkNodeMixin", "TreeError", ] anytree-2.13.0/src/anytree/node/anynode.py000066400000000000000000000062551500063244000204400ustar00rootroot00000000000000from .nodemixin import NodeMixin from .util import _repr class AnyNode(NodeMixin): """ A generic tree node with any `kwargs`. Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute. Other than :any:`Node` this class has no default identifier. It is up to the user to use other attributes for identification. The `parent` attribute refers the parent node: >>> from anytree import AnyNode, RenderTree >>> root = AnyNode(id="root") >>> s0 = AnyNode(id="sub0", parent=root) >>> s0b = AnyNode(id="sub0B", parent=s0, foo=4, bar=109) >>> s0a = AnyNode(id="sub0A", parent=s0) >>> s1 = AnyNode(id="sub1", parent=root) >>> s1a = AnyNode(id="sub1A", parent=s1) >>> s1b = AnyNode(id="sub1B", parent=s1, bar=8) >>> s1c = AnyNode(id="sub1C", parent=s1) >>> s1ca = AnyNode(id="sub1Ca", parent=s1c) >>> root AnyNode(id='root') >>> s0 AnyNode(id='sub0') >>> print(RenderTree(root)) AnyNode(id='root') ├── AnyNode(id='sub0') │ ├── AnyNode(bar=109, foo=4, id='sub0B') │ └── AnyNode(id='sub0A') └── AnyNode(id='sub1') ├── AnyNode(id='sub1A') ├── AnyNode(bar=8, id='sub1B') └── AnyNode(id='sub1C') └── AnyNode(id='sub1Ca') >>> print(RenderTree(root)) AnyNode(id='root') ├── AnyNode(id='sub0') │ ├── AnyNode(bar=109, foo=4, id='sub0B') │ └── AnyNode(id='sub0A') └── AnyNode(id='sub1') ├── AnyNode(id='sub1A') ├── AnyNode(bar=8, id='sub1B') └── AnyNode(id='sub1C') └── AnyNode(id='sub1Ca') Node attributes can be added, modified and deleted the pythonic way: >>> root.new = 'a new attribute' >>> s0b AnyNode(bar=109, foo=4, id='sub0B') >>> s0b.bar = 110 # modified >>> s0b AnyNode(bar=110, foo=4, id='sub0B') >>> del s1b.bar >>> print(RenderTree(root)) AnyNode(id='root', new='a new attribute') ├── AnyNode(id='sub0') │ ├── AnyNode(bar=110, foo=4, id='sub0B') │ └── AnyNode(id='sub0A') └── AnyNode(id='sub1') ├── AnyNode(id='sub1A') ├── AnyNode(id='sub1B') └── AnyNode(id='sub1C') └── AnyNode(id='sub1Ca') The same tree can be constructed by using the `children` attribute: >>> root = AnyNode(id="root", children=[ ... AnyNode(id="sub0", children=[ ... AnyNode(id="sub0B", foo=4, bar=109), ... AnyNode(id="sub0A"), ... ]), ... AnyNode(id="sub1", children=[ ... AnyNode(id="sub1A"), ... AnyNode(id="sub1B", bar=8), ... AnyNode(id="sub1C", children=[ ... AnyNode(id="sub1Ca"), ... ]), ... ]), ... ]) """ def __init__(self, parent=None, children=None, **kwargs): self.__dict__.update(kwargs) self.parent = parent if children: self.children = children def __repr__(self): return _repr(self) anytree-2.13.0/src/anytree/node/exceptions.py000066400000000000000000000001721500063244000211540ustar00rootroot00000000000000class TreeError(RuntimeError): """Tree Error.""" class LoopError(TreeError): """Tree contains infinite loop.""" anytree-2.13.0/src/anytree/node/lightnodemixin.py000066400000000000000000000374511500063244000220270ustar00rootroot00000000000000from anytree.config import ASSERTIONS from anytree.iterators import PreOrderIter from .exceptions import LoopError, TreeError class LightNodeMixin: """ The :any:`LightNodeMixin` behaves identical to :any:`NodeMixin`, but uses `__slots__`. There are some minor differences in the object behaviour. See slots_ for any details. .. _slots: https://docs.python.org/3/reference/datamodel.html#slots The only tree relevant information is the `parent` attribute. If `None` the :any:`LightNodeMixin` is root node. If set to another node, the :any:`LightNodeMixin` becomes the child of it. The `children` attribute can be used likewise. If `None` the :any:`LightNodeMixin` has no children. The `children` attribute can be set to any iterable of :any:`LightNodeMixin` instances. These instances become children of the node. >>> from anytree import LightNodeMixin, RenderTree >>> class MyBaseClass(): # Just an example of a base class ... __slots__ = [] >>> class MyClass(MyBaseClass, LightNodeMixin): # Add Node feature ... __slots__ = ['name', 'length', 'width'] ... def __init__(self, name, length, width, parent=None, children=None): ... super().__init__() ... self.name = name ... self.length = length ... self.width = width ... self.parent = parent ... if children: ... self.children = children Construction via `parent`: >>> my0 = MyClass('my0', 0, 0) >>> my1 = MyClass('my1', 1, 0, parent=my0) >>> my2 = MyClass('my2', 0, 2, parent=my0) >>> for pre, _, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 Construction via `children`: >>> my0 = MyClass('my0', 0, 0, children=[ ... MyClass('my1', 1, 0), ... MyClass('my2', 0, 2), ... ]) >>> for pre, _, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 Both approaches can be mixed: >>> my0 = MyClass('my0', 0, 0, children=[ ... MyClass('my1', 1, 0), ... ]) >>> my2 = MyClass('my2', 0, 2, parent=my0) >>> for pre, _, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 """ __slots__ = ["__children", "__parent"] separator = "/" @property def parent(self): """ Parent Node. On set, the node is detached from any previous parent node and attached to the new node. >>> from anytree import Node, RenderTree >>> udo = Node("Udo") >>> marc = Node("Marc") >>> lian = Node("Lian", parent=marc) >>> print(RenderTree(udo)) Node('/Udo') >>> print(RenderTree(marc)) Node('/Marc') └── Node('/Marc/Lian') **Attach** >>> marc.parent = udo >>> print(RenderTree(udo)) Node('/Udo') └── Node('/Udo/Marc') └── Node('/Udo/Marc/Lian') **Detach** To make a node to a root node, just set this attribute to `None`. >>> marc.is_root False >>> marc.parent = None >>> marc.is_root True """ if hasattr(self, "_LightNodeMixin__parent"): return self.__parent return None @parent.setter def parent(self, value): if hasattr(self, "_LightNodeMixin__parent"): parent = self.__parent else: parent = None if parent is not value: self.__check_loop(value) self.__detach(parent) self.__attach(value) def __check_loop(self, node): if node is not None: if node is self: msg = "Cannot set parent. %r cannot be parent of itself." raise LoopError(msg % (self,)) if any(child is self for child in node.iter_path_reverse()): msg = "Cannot set parent. %r is parent of %r." raise LoopError(msg % (self, node)) def __detach(self, parent): # pylint: disable=W0212,W0238 if parent is not None: self._pre_detach(parent) parentchildren = parent.__children_or_empty if ASSERTIONS: # pragma: no branch assert any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover # ATOMIC START parent.__children = [child for child in parentchildren if child is not self] self.__parent = None # ATOMIC END self._post_detach(parent) def __attach(self, parent): # pylint: disable=W0212 if parent is not None: self._pre_attach(parent) parentchildren = parent.__children_or_empty if ASSERTIONS: # pragma: no branch assert not any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover # ATOMIC START parentchildren.append(self) self.__parent = parent # ATOMIC END self._post_attach(parent) @property def __children_or_empty(self): if not hasattr(self, "_LightNodeMixin__children"): self.__children = [] return self.__children @property def children(self): """ All child nodes. >>> from anytree import Node >>> n = Node("n") >>> a = Node("a", parent=n) >>> b = Node("b", parent=n) >>> c = Node("c", parent=n) >>> n.children (Node('/n/a'), Node('/n/b'), Node('/n/c')) Modifying the children attribute modifies the tree. **Detach** The children attribute can be updated by setting to an iterable. >>> n.children = [a, b] >>> n.children (Node('/n/a'), Node('/n/b')) Node `c` is removed from the tree. In case of an existing reference, the node `c` does not vanish and is the root of its own tree. >>> c Node('/c') **Attach** >>> d = Node("d") >>> d Node('/d') >>> n.children = [a, b, d] >>> n.children (Node('/n/a'), Node('/n/b'), Node('/n/d')) >>> d Node('/n/d') **Duplicate** A node can just be the children once. Duplicates cause a :any:`TreeError`: >>> n.children = [a, b, d, a] Traceback (most recent call last): ... anytree.node.exceptions.TreeError: Cannot add node Node('/n/a') multiple times as child. """ return tuple(self.__children_or_empty) @staticmethod def __check_children(children): seen = set() for child in children: childid = id(child) if childid not in seen: seen.add(childid) else: msg = f"Cannot add node {child!r} multiple times as child." raise TreeError(msg) @children.setter # type: ignore[no-redef] def children(self, children): # convert iterable to tuple children = tuple(children) LightNodeMixin.__check_children(children) # ATOMIC start old_children = self.children del self.children try: self._pre_attach_children(children) for child in children: child.parent = self self._post_attach_children(children) if ASSERTIONS: # pragma: no branch assert len(self.children) == len(children) except Exception: self.children = old_children raise # ATOMIC end @children.deleter # type: ignore[no-redef] def children(self): children = self.children self._pre_detach_children(children) for child in self.children: child.parent = None if ASSERTIONS: # pragma: no branch assert len(self.children) == 0 self._post_detach_children(children) def _pre_detach_children(self, children): """Method call before detaching `children`.""" def _post_detach_children(self, children): """Method call after detaching `children`.""" def _pre_attach_children(self, children): """Method call before attaching `children`.""" def _post_attach_children(self, children): """Method call after attaching `children`.""" @property def path(self): """ Path from root node down to this `Node`. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.path (Node('/Udo'),) >>> marc.path (Node('/Udo'), Node('/Udo/Marc')) >>> lian.path (Node('/Udo'), Node('/Udo/Marc'), Node('/Udo/Marc/Lian')) """ return self._path def iter_path_reverse(self): """ Iterate up the tree from the current node to the root node. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> for node in udo.iter_path_reverse(): ... print(node) Node('/Udo') >>> for node in marc.iter_path_reverse(): ... print(node) Node('/Udo/Marc') Node('/Udo') >>> for node in lian.iter_path_reverse(): ... print(node) Node('/Udo/Marc/Lian') Node('/Udo/Marc') Node('/Udo') """ node = self while node is not None: yield node node = node.parent @property def _path(self): return tuple(reversed(list(self.iter_path_reverse()))) @property def ancestors(self): """ All parent nodes and their parent nodes. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.ancestors () >>> marc.ancestors (Node('/Udo'),) >>> lian.ancestors (Node('/Udo'), Node('/Udo/Marc')) """ if self.parent is None: return () return self.parent.path @property def descendants(self): """ All child nodes and all their child nodes. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> soe = Node("Soe", parent=lian) >>> udo.descendants (Node('/Udo/Marc'), Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Lian/Soe'), Node('/Udo/Marc/Loui')) >>> marc.descendants (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Lian/Soe'), Node('/Udo/Marc/Loui')) >>> lian.descendants (Node('/Udo/Marc/Lian/Soe'),) """ return tuple(PreOrderIter(self))[1:] @property def root(self): """ Tree Root Node. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.root Node('/Udo') >>> marc.root Node('/Udo') >>> lian.root Node('/Udo') """ node = self while node.parent is not None: node = node.parent return node @property def siblings(self): """ Tuple of nodes with the same parent. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> lazy = Node("Lazy", parent=marc) >>> udo.siblings () >>> marc.siblings () >>> lian.siblings (Node('/Udo/Marc/Loui'), Node('/Udo/Marc/Lazy')) >>> loui.siblings (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Lazy')) """ parent = self.parent if parent is None: return () return tuple(node for node in parent.children if node is not self) @property def leaves(self): """ Tuple of all leaf nodes. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> lazy = Node("Lazy", parent=marc) >>> udo.leaves (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Loui'), Node('/Udo/Marc/Lazy')) >>> marc.leaves (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Loui'), Node('/Udo/Marc/Lazy')) """ return tuple(PreOrderIter(self, filter_=lambda node: node.is_leaf)) @property def is_leaf(self): """ `Node` has no children (External Node). >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.is_leaf False >>> marc.is_leaf False >>> lian.is_leaf True """ return len(self.__children_or_empty) == 0 @property def is_root(self): """ `Node` is tree root. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.is_root True >>> marc.is_root False >>> lian.is_root False """ return self.parent is None @property def height(self): """ Number of edges on the longest path to a leaf `Node`. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.height 2 >>> marc.height 1 >>> lian.height 0 """ children = self.__children_or_empty if children: return max(child.height for child in children) + 1 return 0 @property def depth(self): """ Number of edges to the root `Node`. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.depth 0 >>> marc.depth 1 >>> lian.depth 2 """ # count without storing the entire path # pylint: disable=W0631 for depth, _ in enumerate(self.iter_path_reverse()): continue return depth @property def size(self): """ Tree size --- the number of nodes in tree starting at this node. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> soe = Node("Soe", parent=lian) >>> udo.size 5 >>> marc.size 4 >>> lian.size 2 >>> loui.size 1 """ # count without storing the entire path # pylint: disable=W0631 for size, _ in enumerate(PreOrderIter(self), 1): continue return size def _pre_detach(self, parent): """Method call before detaching from `parent`.""" def _post_detach(self, parent): """Method call after detaching from `parent`.""" def _pre_attach(self, parent): """Method call before attaching to `parent`.""" def _post_attach(self, parent): """Method call after attaching to `parent`.""" anytree-2.13.0/src/anytree/node/node.py000066400000000000000000000052711500063244000177250ustar00rootroot00000000000000from .nodemixin import NodeMixin from .util import _repr class Node(NodeMixin): """ A simple tree node with a `name` and any `kwargs`. Args: name: A name or any other object this node can reference to as identifier. Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute. Other than :any:`AnyNode` this class has at least the `name` attribute, to distinguish between different instances. The `parent` attribute refers the parent node: >>> from anytree import Node, RenderTree >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0, foo=4, bar=109) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> s1a = Node("sub1A", parent=s1) >>> s1b = Node("sub1B", parent=s1, bar=8) >>> s1c = Node("sub1C", parent=s1) >>> s1ca = Node("sub1Ca", parent=s1c) >>> print(RenderTree(root)) Node('/root') ├── Node('/root/sub0') │ ├── Node('/root/sub0/sub0B', bar=109, foo=4) │ └── Node('/root/sub0/sub0A') └── Node('/root/sub1') ├── Node('/root/sub1/sub1A') ├── Node('/root/sub1/sub1B', bar=8) └── Node('/root/sub1/sub1C') └── Node('/root/sub1/sub1C/sub1Ca') The same tree can be constructed by using the `children` attribute: >>> root = Node("root", children=[ ... Node("sub0", children=[ ... Node("sub0B", bar=109, foo=4), ... Node("sub0A", children=None), ... ]), ... Node("sub1", children=[ ... Node("sub1A"), ... Node("sub1B", bar=8, children=[]), ... Node("sub1C", children=[ ... Node("sub1Ca"), ... ]), ... ]), ... ]) >>> print(RenderTree(root)) Node('/root') ├── Node('/root/sub0') │ ├── Node('/root/sub0/sub0B', bar=109, foo=4) │ └── Node('/root/sub0/sub0A') └── Node('/root/sub1') ├── Node('/root/sub1/sub1A') ├── Node('/root/sub1/sub1B', bar=8) └── Node('/root/sub1/sub1C') └── Node('/root/sub1/sub1C/sub1Ca') """ def __init__(self, name, parent=None, children=None, **kwargs): self.__dict__.update(kwargs) self.name = name self.parent = parent if children: self.children = children def __repr__(self): args = ["{!r}".format(self.separator.join([""] + [str(node.name) for node in self.path]))] return _repr(self, args=args, nameblacklist=["name"]) anytree-2.13.0/src/anytree/node/nodemixin.py000066400000000000000000000405361500063244000207750ustar00rootroot00000000000000import warnings from anytree.config import ASSERTIONS from anytree.iterators import PreOrderIter from .exceptions import LoopError, TreeError from .lightnodemixin import LightNodeMixin class NodeMixin: """ The :any:`NodeMixin` class extends any Python class to a tree node. The only tree relevant information is the `parent` attribute. If `None` the :any:`NodeMixin` is root node. If set to another node, the :any:`NodeMixin` becomes the child of it. The `children` attribute can be used likewise. If `None` the :any:`NodeMixin` has no children. The `children` attribute can be set to any iterable of :any:`NodeMixin` instances. These instances become children of the node. >>> from anytree import NodeMixin, RenderTree >>> class MyBaseClass(object): # Just an example of a base class ... foo = 4 >>> class MyClass(MyBaseClass, NodeMixin): # Add Node feature ... def __init__(self, name, length, width, parent=None, children=None): ... super(MyClass, self).__init__() ... self.name = name ... self.length = length ... self.width = width ... self.parent = parent ... if children: ... self.children = children Construction via `parent`: >>> my0 = MyClass('my0', 0, 0) >>> my1 = MyClass('my1', 1, 0, parent=my0) >>> my2 = MyClass('my2', 0, 2, parent=my0) >>> for pre, _, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 Construction via `children`: >>> my0 = MyClass('my0', 0, 0, children=[ ... MyClass('my1', 1, 0), ... MyClass('my2', 0, 2), ... ]) >>> for pre, _, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 Both approaches can be mixed: >>> my0 = MyClass('my0', 0, 0, children=[ ... MyClass('my1', 1, 0), ... ]) >>> my2 = MyClass('my2', 0, 2, parent=my0) >>> for pre, _, node in RenderTree(my0): ... treestr = u"%s%s" % (pre, node.name) ... print(treestr.ljust(8), node.length, node.width) my0 0 0 ├── my1 1 0 └── my2 0 2 """ separator = "/" @property def parent(self): """ Parent Node. On set, the node is detached from any previous parent node and attached to the new node. >>> from anytree import Node, RenderTree >>> udo = Node("Udo") >>> marc = Node("Marc") >>> lian = Node("Lian", parent=marc) >>> print(RenderTree(udo)) Node('/Udo') >>> print(RenderTree(marc)) Node('/Marc') └── Node('/Marc/Lian') **Attach** >>> marc.parent = udo >>> print(RenderTree(udo)) Node('/Udo') └── Node('/Udo/Marc') └── Node('/Udo/Marc/Lian') **Detach** To make a node to a root node, just set this attribute to `None`. >>> marc.is_root False >>> marc.parent = None >>> marc.is_root True """ if hasattr(self, "_NodeMixin__parent"): return self.__parent return None @parent.setter def parent(self, value): if value is not None and not isinstance(value, (NodeMixin, LightNodeMixin)): msg = f"Parent node {value!r} is not of type 'NodeMixin'." raise TreeError(msg) if hasattr(self, "_NodeMixin__parent"): parent = self.__parent else: parent = None if parent is not value: self.__check_loop(value) self.__detach(parent) self.__attach(value) def __check_loop(self, node): if node is not None: if node is self: msg = "Cannot set parent. %r cannot be parent of itself." raise LoopError(msg % (self,)) if any(child is self for child in node.iter_path_reverse()): msg = "Cannot set parent. %r is parent of %r." raise LoopError(msg % (self, node)) def __detach(self, parent): # pylint: disable=W0212,W0238 if parent is not None: self._pre_detach(parent) parentchildren = parent.__children_or_empty if ASSERTIONS: # pragma: no branch assert any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover # ATOMIC START parent.__children = [child for child in parentchildren if child is not self] self.__parent = None # ATOMIC END self._post_detach(parent) def __attach(self, parent): # pylint: disable=W0212 if parent is not None: self._pre_attach(parent) parentchildren = parent.__children_or_empty if ASSERTIONS: # pragma: no branch assert not any(child is self for child in parentchildren), "Tree is corrupt." # pragma: no cover # ATOMIC START parentchildren.append(self) self.__parent = parent # ATOMIC END self._post_attach(parent) @property def __children_or_empty(self): if not hasattr(self, "_NodeMixin__children"): self.__children = [] return self.__children @property def children(self): """ All child nodes. >>> from anytree import Node >>> n = Node("n") >>> a = Node("a", parent=n) >>> b = Node("b", parent=n) >>> c = Node("c", parent=n) >>> n.children (Node('/n/a'), Node('/n/b'), Node('/n/c')) Modifying the children attribute modifies the tree. **Detach** The children attribute can be updated by setting to an iterable. >>> n.children = [a, b] >>> n.children (Node('/n/a'), Node('/n/b')) Node `c` is removed from the tree. In case of an existing reference, the node `c` does not vanish and is the root of its own tree. >>> c Node('/c') **Attach** >>> d = Node("d") >>> d Node('/d') >>> n.children = [a, b, d] >>> n.children (Node('/n/a'), Node('/n/b'), Node('/n/d')) >>> d Node('/n/d') **Duplicate** A node can just be the children once. Duplicates cause a :any:`TreeError`: >>> n.children = [a, b, d, a] Traceback (most recent call last): ... anytree.node.exceptions.TreeError: Cannot add node Node('/n/a') multiple times as child. """ return tuple(self.__children_or_empty) @staticmethod def __check_children(children): seen = set() for child in children: if not isinstance(child, (NodeMixin, LightNodeMixin)): msg = f"Cannot add non-node object {child!r}. It is not a subclass of 'NodeMixin'." raise TreeError(msg) childid = id(child) if childid not in seen: seen.add(childid) else: msg = f"Cannot add node {child!r} multiple times as child." raise TreeError(msg) @children.setter # type: ignore[no-redef] def children(self, children): # convert iterable to tuple children = tuple(children) NodeMixin.__check_children(children) # ATOMIC start old_children = self.children del self.children try: self._pre_attach_children(children) for child in children: child.parent = self self._post_attach_children(children) if ASSERTIONS: # pragma: no branch assert len(self.children) == len(children) except Exception: self.children = old_children raise # ATOMIC end @children.deleter # type: ignore[no-redef] def children(self): children = self.children self._pre_detach_children(children) for child in self.children: child.parent = None if ASSERTIONS: # pragma: no branch assert len(self.children) == 0 self._post_detach_children(children) def _pre_detach_children(self, children): """Method call before detaching `children`.""" def _post_detach_children(self, children): """Method call after detaching `children`.""" def _pre_attach_children(self, children): """Method call before attaching `children`.""" def _post_attach_children(self, children): """Method call after attaching `children`.""" @property def path(self): """ Path from root node down to this `Node`. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.path (Node('/Udo'),) >>> marc.path (Node('/Udo'), Node('/Udo/Marc')) >>> lian.path (Node('/Udo'), Node('/Udo/Marc'), Node('/Udo/Marc/Lian')) """ return self._path def iter_path_reverse(self): """ Iterate up the tree from the current node to the root node. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> for node in udo.iter_path_reverse(): ... print(node) Node('/Udo') >>> for node in marc.iter_path_reverse(): ... print(node) Node('/Udo/Marc') Node('/Udo') >>> for node in lian.iter_path_reverse(): ... print(node) Node('/Udo/Marc/Lian') Node('/Udo/Marc') Node('/Udo') """ node = self while node is not None: yield node node = node.parent @property def _path(self): return tuple(reversed(list(self.iter_path_reverse()))) @property def ancestors(self): """ All parent nodes and their parent nodes. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.ancestors () >>> marc.ancestors (Node('/Udo'),) >>> lian.ancestors (Node('/Udo'), Node('/Udo/Marc')) """ if self.parent is None: return () return self.parent.path @property def anchestors(self): """ All parent nodes and their parent nodes - see :any:`ancestors`. The attribute `anchestors` is just a typo of `ancestors`. Please use `ancestors`. This attribute will be removed in the 3.0.0 release. """ warnings.warn(".anchestors was a typo and will be removed in version 3.0.0", DeprecationWarning, stacklevel=2) return self.ancestors @property def descendants(self): """ All child nodes and all their child nodes. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> soe = Node("Soe", parent=lian) >>> udo.descendants (Node('/Udo/Marc'), Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Lian/Soe'), Node('/Udo/Marc/Loui')) >>> marc.descendants (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Lian/Soe'), Node('/Udo/Marc/Loui')) >>> lian.descendants (Node('/Udo/Marc/Lian/Soe'),) """ return tuple(PreOrderIter(self))[1:] @property def root(self): """ Tree Root Node. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.root Node('/Udo') >>> marc.root Node('/Udo') >>> lian.root Node('/Udo') """ node = self while node.parent is not None: node = node.parent return node @property def siblings(self): """ Tuple of nodes with the same parent. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> lazy = Node("Lazy", parent=marc) >>> udo.siblings () >>> marc.siblings () >>> lian.siblings (Node('/Udo/Marc/Loui'), Node('/Udo/Marc/Lazy')) >>> loui.siblings (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Lazy')) """ parent = self.parent if parent is None: return () return tuple(node for node in parent.children if node is not self) @property def leaves(self): """ Tuple of all leaf nodes. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> lazy = Node("Lazy", parent=marc) >>> udo.leaves (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Loui'), Node('/Udo/Marc/Lazy')) >>> marc.leaves (Node('/Udo/Marc/Lian'), Node('/Udo/Marc/Loui'), Node('/Udo/Marc/Lazy')) """ return tuple(PreOrderIter(self, filter_=lambda node: node.is_leaf)) @property def is_leaf(self): """ `Node` has no children (External Node). >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.is_leaf False >>> marc.is_leaf False >>> lian.is_leaf True """ return len(self.__children_or_empty) == 0 @property def is_root(self): """ `Node` is tree root. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.is_root True >>> marc.is_root False >>> lian.is_root False """ return self.parent is None @property def height(self): """ Number of edges on the longest path to a leaf `Node`. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.height 2 >>> marc.height 1 >>> lian.height 0 """ children = self.__children_or_empty if children: return max(child.height for child in children) + 1 return 0 @property def depth(self): """ Number of edges to the root `Node`. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> udo.depth 0 >>> marc.depth 1 >>> lian.depth 2 """ # count without storing the entire path # pylint: disable=W0631 for depth, _ in enumerate(self.iter_path_reverse()): continue return depth @property def size(self): """ Tree size --- the number of nodes in tree starting at this node. >>> from anytree import Node >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> loui = Node("Loui", parent=marc) >>> soe = Node("Soe", parent=lian) >>> udo.size 5 >>> marc.size 4 >>> lian.size 2 >>> loui.size 1 """ # count without storing the entire path # pylint: disable=W0631 for size, _ in enumerate(PreOrderIter(self), 1): continue return size def _pre_detach(self, parent): """Method call before detaching from `parent`.""" def _post_detach(self, parent): """Method call after detaching from `parent`.""" def _pre_attach(self, parent): """Method call before attaching to `parent`.""" def _post_attach(self, parent): """Method call after attaching to `parent`.""" anytree-2.13.0/src/anytree/node/symlinknode.py000066400000000000000000000030361500063244000213310ustar00rootroot00000000000000from .symlinknodemixin import SymlinkNodeMixin from .util import _repr class SymlinkNode(SymlinkNodeMixin): """ Tree node which references to another tree node. Args: target: Symbolic Link Target. Another tree node, which is referred to. Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as attribute **in** `target`. The :any:`SymlinkNode` has its own parent and its own child nodes. All other attribute accesses are just forwarded to the target node. >>> from anytree import SymlinkNode, Node, RenderTree >>> root = Node("root") >>> s1 = Node("sub1", parent=root, bar=17) >>> l = SymlinkNode(s1, parent=root, baz=18) >>> l0 = Node("l0", parent=l) >>> print(RenderTree(root)) Node('/root') ├── Node('/root/sub1', bar=17, baz=18) └── SymlinkNode(Node('/root/sub1', bar=17, baz=18)) └── Node('/root/sub1/l0') Any modifications on the target node are also available on the linked node and vice-versa: >>> s1.foo = 4 >>> s1.foo 4 >>> l.foo 4 >>> l.foo = 9 >>> s1.foo 9 >>> l.foo 9 """ def __init__(self, target, parent=None, children=None, **kwargs): self.target = target self.target.__dict__.update(kwargs) self.parent = parent if children: self.children = children def __repr__(self): return _repr(self, [repr(self.target)], nameblacklist=("target",)) anytree-2.13.0/src/anytree/node/symlinknodemixin.py000066400000000000000000000036511500063244000224010ustar00rootroot00000000000000from .nodemixin import NodeMixin class SymlinkNodeMixin(NodeMixin): """ The :any:`SymlinkNodeMixin` class extends any Python class to a symbolic link to a tree node. The class **MUST** have a `target` attribute referring to another tree node. The :any:`SymlinkNodeMixin` class has its own parent and its own child nodes. All other attribute accesses are just forwarded to the target node. A minimal implementation looks like (see :any:`SymlinkNode` for a full implementation): >>> from anytree import SymlinkNodeMixin, Node, RenderTree >>> class SymlinkNode(SymlinkNodeMixin): ... def __init__(self, target, parent=None, children=None): ... self.target = target ... self.parent = parent ... if children: ... self.children = children ... def __repr__(self): ... return "SymlinkNode(%r)" % (self.target) >>> root = Node("root") >>> s1 = Node("sub1", parent=root) >>> l = SymlinkNode(s1, parent=root) >>> l0 = Node("l0", parent=l) >>> print(RenderTree(root)) Node('/root') ├── Node('/root/sub1') └── SymlinkNode(Node('/root/sub1')) └── Node('/root/sub1/l0') Any modifications on the target node are also available on the linked node and vice-versa: >>> s1.foo = 4 >>> s1.foo 4 >>> l.foo 4 >>> l.foo = 9 >>> s1.foo 9 >>> l.foo 9 """ def __getattr__(self, name): if name in ("_NodeMixin__parent", "_NodeMixin__children"): return super().__getattr__(name) if name == "__setstate__": raise AttributeError(name) return getattr(self.target, name) def __setattr__(self, name, value): if name in ("_NodeMixin__parent", "_NodeMixin__children", "parent", "children", "target"): super().__setattr__(name, value) else: setattr(self.target, name, value) anytree-2.13.0/src/anytree/node/util.py000066400000000000000000000006561500063244000177570ustar00rootroot00000000000000def _repr(node, args=None, nameblacklist=None): classname = node.__class__.__name__ args = args or [] nameblacklist = nameblacklist or [] for key, value in filter( lambda item: not item[0].startswith("_") and item[0] not in nameblacklist, sorted(node.__dict__.items(), key=lambda item: item[0]), ): args.append(f"{key}={value!r}") return "{}({})".format(classname, ", ".join(args)) anytree-2.13.0/src/anytree/render.py000066400000000000000000000237631500063244000173400ustar00rootroot00000000000000""" Tree Rendering. * :any:`RenderTree` using the following styles: * :any:`AsciiStyle` * :any:`ContStyle` * :any:`ContRoundStyle` * :any:`DoubleStyle` """ import collections from .config import ASSERTIONS Row = collections.namedtuple("Row", ("pre", "fill", "node")) class AbstractStyle: """ Tree Render Style. Args: vertical: Sign for vertical line. cont: Chars for a continued branch. end: Chars for the last branch. """ def __init__(self, vertical, cont, end): super().__init__() self.vertical = vertical self.cont = cont self.end = end if ASSERTIONS: # pragma: no branch assert len(cont) == len(vertical) == len(end), ( f"'{vertical}', '{cont}' and '{end}' need to have equal length" ) @property def empty(self): """Empty string as placeholder.""" return " " * len(self.end) def __repr__(self): classname = self.__class__.__name__ return f"{classname}()" class AsciiStyle(AbstractStyle): """ Ascii style. >>> from anytree import Node, RenderTree >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> print(RenderTree(root, style=AsciiStyle())) Node('/root') |-- Node('/root/sub0') | |-- Node('/root/sub0/sub0B') | +-- Node('/root/sub0/sub0A') +-- Node('/root/sub1') """ def __init__(self): super().__init__("| ", "|-- ", "+-- ") class ContStyle(AbstractStyle): """ Continued style, without gaps. >>> from anytree import Node, RenderTree >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> print(RenderTree(root, style=ContStyle())) Node('/root') ├── Node('/root/sub0') │ ├── Node('/root/sub0/sub0B') │ └── Node('/root/sub0/sub0A') └── Node('/root/sub1') """ def __init__(self): super().__init__("\u2502 ", "\u251c\u2500\u2500 ", "\u2514\u2500\u2500 ") CONT_STYLE = ContStyle() class ContRoundStyle(AbstractStyle): """ Continued style, without gaps, round edges. >>> from anytree import Node, RenderTree >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> print(RenderTree(root, style=ContRoundStyle())) Node('/root') ├── Node('/root/sub0') │ ├── Node('/root/sub0/sub0B') │ ╰── Node('/root/sub0/sub0A') ╰── Node('/root/sub1') """ def __init__(self): super().__init__("\u2502 ", "\u251c\u2500\u2500 ", "\u2570\u2500\u2500 ") class DoubleStyle(AbstractStyle): """ Double line style, without gaps. >>> from anytree import Node, RenderTree >>> root = Node("root") >>> s0 = Node("sub0", parent=root) >>> s0b = Node("sub0B", parent=s0) >>> s0a = Node("sub0A", parent=s0) >>> s1 = Node("sub1", parent=root) >>> print(RenderTree(root, style=DoubleStyle)) Node('/root') â• â•�â•� Node('/root/sub0') â•‘ â• â•�â•� Node('/root/sub0/sub0B') â•‘ ╚â•�â•� Node('/root/sub0/sub0A') ╚â•�â•� Node('/root/sub1') """ def __init__(self): super().__init__("\u2551 ", "\u2560\u2550\u2550 ", "\u255a\u2550\u2550 ") class RenderTree: """ Render tree starting at `node`. Keyword Args: style (AbstractStyle): Render Style. childiter: Child iterator. maxlevel: Limit rendering to this depth. :any:`RenderTree` is an iterator, returning a tuple with 3 items: `pre` tree prefix. `fill` filling for multiline entries. `node` :any:`NodeMixin` object. It is up to the user to assemble these parts to a whole. >>> from anytree import Node, RenderTree >>> root = Node("root", lines=["c0fe", "c0de"]) >>> s0 = Node("sub0", parent=root, lines=["ha", "ba"]) >>> s0b = Node("sub0B", parent=s0, lines=["1", "2", "3"]) >>> s0a = Node("sub0A", parent=s0, lines=["a", "b"]) >>> s1 = Node("sub1", parent=root, lines=["Z"]) Simple one line: >>> for pre, _, node in RenderTree(root): ... print("%s%s" % (pre, node.name)) root ├── sub0 │ ├── sub0B │ └── sub0A └── sub1 Multiline: >>> for pre, fill, node in RenderTree(root): ... print("%s%s" % (pre, node.lines[0])) ... for line in node.lines[1:]: ... print("%s%s" % (fill, line)) c0fe c0de ├── ha │ ba │ ├── 1 │ │ 2 │ │ 3 │ └── a │ b └── Z `maxlevel` limits the depth of the tree: >>> print(RenderTree(root, maxlevel=2)) Node('/root', lines=['c0fe', 'c0de']) ├── Node('/root/sub0', lines=['ha', 'ba']) └── Node('/root/sub1', lines=['Z']) The `childiter` is responsible for iterating over child nodes at the same level. An reversed order can be achieved by using `reversed`. >>> for row in RenderTree(root, childiter=reversed): ... print("%s%s" % (row.pre, row.node.name)) root ├── sub1 └── sub0 ├── sub0A └── sub0B Or writing your own sort function: >>> def mysort(items): ... return sorted(items, key=lambda item: item.name) >>> for row in RenderTree(root, childiter=mysort): ... print("%s%s" % (row.pre, row.node.name)) root ├── sub0 │ ├── sub0A │ └── sub0B └── sub1 :any:`by_attr` simplifies attribute rendering and supports multiline: >>> print(RenderTree(root).by_attr()) root ├── sub0 │ ├── sub0B │ └── sub0A └── sub1 >>> print(RenderTree(root).by_attr("lines")) c0fe c0de ├── ha │ ba │ ├── 1 │ │ 2 │ │ 3 │ └── a │ b └── Z And can be a function: >>> print(RenderTree(root).by_attr(lambda n: " ".join(n.lines))) c0fe c0de ├── ha ba │ ├── 1 2 3 │ └── a b └── Z """ def __init__(self, node, style=CONT_STYLE, childiter=list, maxlevel=None): if not isinstance(style, AbstractStyle): style = style() self.node = node self.style = style self.childiter = childiter self.maxlevel = maxlevel def __iter__(self): return self.__next(self.node, ()) def __next(self, node, continues, level=0): yield RenderTree.__item(node, continues, self.style) level += 1 if self.maxlevel is None or level < self.maxlevel: children = node.children if children: children = self.childiter(children) for child, is_last in _is_last(children): yield from self.__next(child, (*continues, not is_last), level=level) @staticmethod def __item(node, continues, style): if not continues: return Row("", "", node) items = [style.vertical if cont else style.empty for cont in continues] indent = "".join(items[:-1]) branch = style.cont if continues[-1] else style.end pre = indent + branch fill = "".join(items) return Row(pre, fill, node) def __str__(self): def get(): for row in self: lines = repr(row.node).splitlines() or [""] yield f"{row.pre}{lines[0]}" for line in lines[1:]: yield f"{row.fill}{line}" return "\n".join(get()) def __repr__(self): classname = self.__class__.__name__ args = [repr(self.node), f"style={self.style!r}", f"childiter={self.childiter!r}"] return "{}({})".format(classname, ", ".join(args)) def by_attr(self, attrname="name"): """ Return rendered tree with node attribute `attrname`. >>> from anytree import AnyNode, RenderTree >>> root = AnyNode(id="root") >>> s0 = AnyNode(id="sub0", parent=root) >>> s0b = AnyNode(id="sub0B", parent=s0, foo=4, bar=109) >>> s0a = AnyNode(id="sub0A", parent=s0) >>> s1 = AnyNode(id="sub1", parent=root) >>> s1a = AnyNode(id="sub1A", parent=s1) >>> s1b = AnyNode(id="sub1B", parent=s1, bar=8) >>> s1c = AnyNode(id="sub1C", parent=s1) >>> s1ca = AnyNode(id="sub1Ca", parent=s1c) >>> print(RenderTree(root).by_attr('id')) root ├── sub0 │ ├── sub0B │ └── sub0A └── sub1 ├── sub1A ├── sub1B └── sub1C └── sub1Ca """ def get(): if callable(attrname): for row in self: attr = attrname(row.node) yield from _format_row_any(row, attr) else: for row in self: attr = getattr(row.node, attrname, "") yield from _format_row_any(row, attr) return "\n".join(get()) def _format_row_any(row, attr): if isinstance(attr, (list, tuple)): lines = attr or [""] else: lines = str(attr).splitlines() or [""] yield f"{row.pre}{lines[0]}" for line in lines[1:]: yield f"{row.fill}{line}" def _is_last(iterable): iter_ = iter(iterable) try: nextitem = next(iter_) except StopIteration: pass else: item = nextitem while True: try: nextitem = next(iter_) yield item, False except StopIteration: yield nextitem, True break item = nextitem anytree-2.13.0/src/anytree/resolver.py000066400000000000000000000264601500063244000177170ustar00rootroot00000000000000import re from anytree.iterators.preorderiter import PreOrderIter from .config import ASSERTIONS _MAXCACHE = 20 class Resolver: """ Resolve :any:`NodeMixin` paths using attribute `pathattr`. Keyword Args: name (str): Name of the node attribute to be used for resolving ignorecase (bool): Enable case insensisitve handling. relax (bool): Do not raise an exception. """ _match_cache = {} # type: ignore[var-annotated] # noqa: RUF012 def __init__(self, pathattr="name", ignorecase=False, relax=False): super().__init__() self.pathattr = pathattr self.ignorecase = ignorecase self.relax = relax def get(self, node, path): """ Return instance at `path`. An example module tree: >>> from anytree import Node >>> top = Node("top", parent=None) >>> sub0 = Node("sub0", parent=top) >>> sub0sub0 = Node("sub0sub0", parent=sub0) >>> sub0sub1 = Node("sub0sub1", parent=sub0) >>> sub1 = Node("sub1", parent=top) A resolver using the `name` attribute: >>> resolver = Resolver('name') >>> relaxedresolver = Resolver('name', relax=True) # never generate exceptions Relative paths: >>> resolver.get(top, "sub0/sub0sub0") Node('/top/sub0/sub0sub0') >>> resolver.get(sub1, "..") Node('/top') >>> resolver.get(sub1, "../sub0/sub0sub1") Node('/top/sub0/sub0sub1') >>> resolver.get(sub1, ".") Node('/top/sub1') >>> resolver.get(sub1, "") Node('/top/sub1') >>> resolver.get(top, "sub2") Traceback (most recent call last): ... anytree.resolver.ChildResolverError: Node('/top') has no child sub2. Children are: 'sub0', 'sub1'. >>> print(relaxedresolver.get(top, "sub2")) None Absolute paths: >>> resolver.get(sub0sub0, "/top") Node('/top') >>> resolver.get(sub0sub0, "/top/sub0") Node('/top/sub0') >>> resolver.get(sub0sub0, "/") Traceback (most recent call last): ... anytree.resolver.ResolverError: root node missing. root is '/top'. >>> print(relaxedresolver.get(sub0sub0, "/")) None >>> resolver.get(sub0sub0, "/bar") Traceback (most recent call last): ... anytree.resolver.ResolverError: unknown root node '/bar'. root is '/top'. >>> print(relaxedresolver.get(sub0sub0, "/bar")) None Going above the root node raises a :any:`RootResolverError`: >>> resolver.get(top, "..") Traceback (most recent call last): ... anytree.resolver.RootResolverError: Cannot go above root node Node('/top') .. note:: Please not that :any:`get()` returned `None` in exactly that case above, which was a bug until version 1.8.1. Case insensitive matching: >>> resolver.get(top, '/TOP') Traceback (most recent call last): ... anytree.resolver.ResolverError: unknown root node '/TOP'. root is '/top'. >>> ignorecaseresolver = Resolver('name', ignorecase=True) >>> ignorecaseresolver.get(top, '/TOp') Node('/top') """ node, parts = self.__start(node, path, self.__cmp) if node is None and self.relax: return None for part in parts: if part == "..": parent = node.parent if parent is None: if self.relax: return None raise RootResolverError(node) node = parent elif part in ("", "."): pass else: node = self.__get(node, part) return node def __get(self, node, name): namestr = str(name) for child in node.children: if self.__cmp(_getattr(child, self.pathattr), namestr): return child if self.relax: return None raise ChildResolverError(node, name, self.pathattr) def glob(self, node, path): """ Return instances at `path` supporting wildcards. Behaves identical to :any:`get`, but accepts wildcards and returns a list of found nodes. * `*` matches any characters, except '/'. * `?` matches a single character, except '/'. An example module tree: >>> from anytree import Node >>> top = Node("top", parent=None) >>> sub0 = Node("sub0", parent=top) >>> sub0sub0 = Node("sub0", parent=sub0) >>> sub0sub1 = Node("sub1", parent=sub0) >>> sub1 = Node("sub1", parent=top) >>> sub1sub0 = Node("sub0", parent=sub1) A resolver using the `name` attribute: >>> resolver = Resolver('name') >>> relaxedresolver = Resolver('name', relax=True) # never generate exceptions Relative paths: >>> resolver.glob(top, "sub0/sub?") [Node('/top/sub0/sub0'), Node('/top/sub0/sub1')] >>> resolver.glob(sub1, ".././*") [Node('/top/sub0'), Node('/top/sub1')] >>> resolver.glob(top, "*/*") [Node('/top/sub0/sub0'), Node('/top/sub0/sub1'), Node('/top/sub1/sub0')] >>> resolver.glob(top, "*/sub0") [Node('/top/sub0/sub0'), Node('/top/sub1/sub0')] >>> resolver.glob(top, "sub1/sub1") Traceback (most recent call last): ... anytree.resolver.ChildResolverError: Node('/top/sub1') has no child sub1. Children are: 'sub0'. >>> relaxedresolver.glob(top, "sub1/sub1") [] Non-matching wildcards are no error: >>> resolver.glob(top, "bar*") [] >>> resolver.glob(top, "sub2") Traceback (most recent call last): ... anytree.resolver.ChildResolverError: Node('/top') has no child sub2. Children are: 'sub0', 'sub1'. >>> relaxedresolver.glob(top, "sub2") [] Absolute paths: >>> resolver.glob(sub0sub0, "/top/*") [Node('/top/sub0'), Node('/top/sub1')] >>> resolver.glob(sub0sub0, "/") Traceback (most recent call last): ... anytree.resolver.ResolverError: root node missing. root is '/top'. >>> relaxedresolver.glob(sub0sub0, "/") [] >>> resolver.glob(sub0sub0, "/bar") Traceback (most recent call last): ... anytree.resolver.ResolverError: unknown root node '/bar'. root is '/top'. Going above the root node raises a :any:`RootResolverError`: >>> resolver.glob(top, "..") Traceback (most recent call last): ... anytree.resolver.RootResolverError: Cannot go above root node Node('/top') >>> relaxedresolver.glob(top, "..") [] """ node, parts = self.__start(node, path, self.__match) if node is None and self.relax: return [] return self.__glob(node, parts) def __start(self, node, path, cmp_): sep = node.separator parts = path.split(sep) # resolve root if path.startswith(sep): node = node.root rootpart = _getattr(node, self.pathattr) parts.pop(0) if not parts[0]: if self.relax: return None, None msg = "root node missing. root is '%s%s'." raise ResolverError(node, "", msg % (sep, str(rootpart))) if not cmp_(rootpart, parts[0]): if self.relax: return None, None msg = "unknown root node '%s%s'. root is '%s%s'." raise ResolverError(node, "", msg % (sep, parts[0], sep, str(rootpart))) parts.pop(0) return node, parts def __glob(self, node, parts): if ASSERTIONS: # pragma: no branch assert node is not None if not parts: return [node] name = parts[0] remainder = parts[1:] # handle relative if name == "..": parent = node.parent if parent is None: if self.relax: return [] raise RootResolverError(node) return self.__glob(parent, remainder) if name in ("", "."): return self.__glob(node, remainder) # handle recursive if name == "**": matches = [] for subnode in PreOrderIter(node): try: for match in self.__glob(subnode, remainder): if match not in matches: matches.append(match) except ChildResolverError: pass return matches matches = self.__find(node, name, remainder) if not matches and not Resolver.is_wildcard(name) and not self.relax: raise ChildResolverError(node, name, self.pathattr) return matches def __find(self, node, pat, remainder): matches = [] for child in node.children: name = _getattr(child, self.pathattr) try: if self.__match(name, pat): if remainder: matches += self.__glob(child, remainder) else: matches.append(child) except ResolverError as exc: if not Resolver.is_wildcard(pat): raise exc return matches @staticmethod def is_wildcard(path): """Return `True` is a wildcard.""" return "?" in path or "*" in path def __match(self, name, pat): k = (pat, self.ignorecase) try: re_pat = Resolver._match_cache[k] except KeyError: res = Resolver.__translate(pat) if len(Resolver._match_cache) >= _MAXCACHE: Resolver._match_cache.clear() flags = 0 if self.ignorecase: flags |= re.IGNORECASE Resolver._match_cache[k] = re_pat = re.compile(res, flags=flags) return re_pat.match(name) is not None def __cmp(self, name, pat): if self.ignorecase: return name.upper() == pat.upper() return name == pat @staticmethod def __translate(pat): re_pat = "" for char in pat: if char == "*": re_pat += ".*" elif char == "?": re_pat += "." else: re_pat += re.escape(char) return r"(?ms)" + re_pat + r"\Z" class ResolverError(RuntimeError): def __init__(self, node, child, msg): """Resolve Error at `node` handling `child`.""" super().__init__(msg) self.node = node self.child = child class RootResolverError(ResolverError): def __init__(self, root): """Root Resolve Error, cannot go above root node.""" msg = f"Cannot go above root node {root!r}" super().__init__(root, None, msg) class ChildResolverError(ResolverError): def __init__(self, node, child, pathattr): """Child Resolve Error at `node` handling `child`.""" names = [repr(_getattr(c, pathattr)) for c in node.children] msg = "{!r} has no child {}. Children are: {}.".format(node, child, ", ".join(names)) super().__init__(node, child, msg) def _getattr(node, name): return str(getattr(node, name, None)) anytree-2.13.0/src/anytree/search.py000066400000000000000000000162061500063244000173200ustar00rootroot00000000000000""" Node Searching. .. note:: You can speed-up node searching, by installing https://pypi.org/project/fastcache/ and using :any:`cachedsearch`. """ from anytree.iterators import PreOrderIter def findall(node, filter_=None, stop=None, maxlevel=None, mincount=None, maxcount=None): """ Search nodes matching `filter_` but stop at `maxlevel` or `stop`. Return tuple with matching nodes. Args: node: top node, start searching. Keyword Args: filter_: function called with every `node` as argument, `node` is returned if `True`. stop: stop iteration at `node` if `stop` function returns `True` for `node`. maxlevel (int): maximum descending in the node hierarchy. mincount (int): minimum number of nodes. maxcount (int): maximum number of nodes. Example tree: >>> from anytree import Node, RenderTree, AsciiStyle >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> findall(f, filter_=lambda node: node.name in ("a", "b")) (Node('/f/b'), Node('/f/b/a')) >>> findall(f, filter_=lambda node: d in node.path) (Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e')) The number of matches can be limited: >>> findall(f, filter_=lambda node: d in node.path, mincount=4) # doctest: +ELLIPSIS Traceback (most recent call last): ... anytree.search.CountError: Expecting at least 4 elements, but found 3. ... Node('/f/b/d/e')) >>> findall(f, filter_=lambda node: d in node.path, maxcount=2) # doctest: +ELLIPSIS Traceback (most recent call last): ... anytree.search.CountError: Expecting 2 elements at maximum, but found 3. ... Node('/f/b/d/e')) """ return _findall(node, filter_=filter_, stop=stop, maxlevel=maxlevel, mincount=mincount, maxcount=maxcount) def findall_by_attr(node, value, name="name", maxlevel=None, mincount=None, maxcount=None): """ Search nodes with attribute `name` having `value` but stop at `maxlevel`. Return tuple with matching nodes. Args: node: top node, start searching. value: value which need to match Keyword Args: name (str): attribute name need to match maxlevel (int): maximum descending in the node hierarchy. mincount (int): minimum number of nodes. maxcount (int): maximum number of nodes. Example tree: >>> from anytree import Node, RenderTree, AsciiStyle >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> findall_by_attr(f, "d") (Node('/f/b/d'),) """ return _findall( node, filter_=lambda n: _filter_by_name(n, name, value), maxlevel=maxlevel, mincount=mincount, maxcount=maxcount, ) def find(node, filter_=None, stop=None, maxlevel=None): """ Search for *single* node matching `filter_` but stop at `maxlevel` or `stop`. Return matching node. Args: node: top node, start searching. Keyword Args: filter_: function called with every `node` as argument, `node` is returned if `True`. stop: stop iteration at `node` if `stop` function returns `True` for `node`. maxlevel (int): maximum descending in the node hierarchy. Example tree: >>> from anytree import Node, RenderTree, AsciiStyle >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> find(f, lambda node: node.name == "d") Node('/f/b/d') >>> find(f, lambda node: node.name == "z") >>> find(f, lambda node: b in node.path) # doctest: +ELLIPSIS Traceback (most recent call last): ... anytree.search.CountError: Expecting 1 elements at maximum, but found 5. (Node('/f/b')... Node('/f/b/d/e')) """ return _find(node, filter_=filter_, stop=stop, maxlevel=maxlevel) def find_by_attr(node, value, name="name", maxlevel=None): """ Search for *single* node with attribute `name` having `value` but stop at `maxlevel`. Return matching node. Args: node: top node, start searching. value: value which need to match Keyword Args: name (str): attribute name need to match maxlevel (int): maximum descending in the node hierarchy. Example tree: >>> from anytree import Node, RenderTree, AsciiStyle >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d, foo=4) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle()).by_attr()) f |-- b | |-- a | +-- d | |-- c | +-- e +-- g +-- i +-- h >>> find_by_attr(f, "d") Node('/f/b/d') >>> find_by_attr(f, name="foo", value=4) Node('/f/b/d/c', foo=4) >>> find_by_attr(f, name="foo", value=8) """ return _find(node, filter_=lambda n: _filter_by_name(n, name, value), maxlevel=maxlevel) def _find(node, filter_, stop=None, maxlevel=None): items = _findall(node, filter_, stop=stop, maxlevel=maxlevel, maxcount=1) return items[0] if items else None def _findall(node, filter_, stop=None, maxlevel=None, mincount=None, maxcount=None): result = tuple(PreOrderIter(node, filter_, stop, maxlevel)) resultlen = len(result) if mincount is not None and resultlen < mincount: msg = "Expecting at least %d elements, but found %d." raise CountError(msg % (mincount, resultlen), result) if maxcount is not None and resultlen > maxcount: msg = "Expecting %d elements at maximum, but found %d." raise CountError(msg % (maxcount, resultlen), result) return result def _filter_by_name(node, name, value): try: return getattr(node, name) == value except AttributeError: return False class CountError(RuntimeError): def __init__(self, msg, result): """Error raised on `mincount` or `maxcount` mismatch.""" if result: msg += " " + repr(result) super().__init__(msg) anytree-2.13.0/src/anytree/util/000077500000000000000000000000001500063244000164515ustar00rootroot00000000000000anytree-2.13.0/src/anytree/util/__init__.py000066400000000000000000000043461500063244000205710ustar00rootroot00000000000000"""Utilities.""" def commonancestors(*nodes): """ Determine common ancestors of `nodes`. >>> from anytree import Node, util >>> udo = Node("Udo") >>> marc = Node("Marc", parent=udo) >>> lian = Node("Lian", parent=marc) >>> dan = Node("Dan", parent=udo) >>> jet = Node("Jet", parent=dan) >>> jan = Node("Jan", parent=dan) >>> joe = Node("Joe", parent=dan) >>> util.commonancestors(jet, joe) (Node('/Udo'), Node('/Udo/Dan')) >>> util.commonancestors(jet, marc) (Node('/Udo'),) >>> util.commonancestors(jet) (Node('/Udo'), Node('/Udo/Dan')) >>> util.commonancestors() () """ ancestors = [node.ancestors for node in nodes] common = [] for parentnodes in zip(*ancestors): parentnode = parentnodes[0] if all(parentnode is p for p in parentnodes[1:]): common.append(parentnode) else: break return tuple(common) def leftsibling(node): """ Return Left Sibling of `node`. >>> from anytree import Node, util >>> dan = Node("Dan") >>> jet = Node("Jet", parent=dan) >>> jan = Node("Jan", parent=dan) >>> joe = Node("Joe", parent=dan) >>> print(util.leftsibling(dan)) None >>> print(util.leftsibling(jet)) None >>> print(util.leftsibling(jan)) Node('/Dan/Jet') >>> print(util.leftsibling(joe)) Node('/Dan/Jan') """ if node.parent: pchildren = node.parent.children idx = pchildren.index(node) if idx: return pchildren[idx - 1] return None def rightsibling(node): """ Return Right Sibling of `node`. >>> from anytree import Node, util >>> dan = Node("Dan") >>> jet = Node("Jet", parent=dan) >>> jan = Node("Jan", parent=dan) >>> joe = Node("Joe", parent=dan) >>> print(util.rightsibling(dan)) None >>> print(util.rightsibling(jet)) Node('/Dan/Jan') >>> print(util.rightsibling(jan)) Node('/Dan/Joe') >>> print(util.rightsibling(joe)) None """ if node.parent: pchildren = node.parent.children idx = pchildren.index(node) try: return pchildren[idx + 1] except IndexError: return None else: return None anytree-2.13.0/src/anytree/walker.py000066400000000000000000000054151500063244000173400ustar00rootroot00000000000000from .config import ASSERTIONS class Walker: """Walk from one node to another.""" @staticmethod def walk(start, end): """ Walk from `start` node to `end` node. Returns: (upwards, common, downwards): `upwards` is a list of nodes to go upward to. `common` top node. `downwards` is a list of nodes to go downward to. Raises: WalkError: on no common root node. Example: >>> from anytree import Node, RenderTree, AsciiStyle >>> f = Node("f") >>> b = Node("b", parent=f) >>> a = Node("a", parent=b) >>> d = Node("d", parent=b) >>> c = Node("c", parent=d) >>> e = Node("e", parent=d) >>> g = Node("g", parent=f) >>> i = Node("i", parent=g) >>> h = Node("h", parent=i) >>> print(RenderTree(f, style=AsciiStyle())) Node('/f') |-- Node('/f/b') | |-- Node('/f/b/a') | +-- Node('/f/b/d') | |-- Node('/f/b/d/c') | +-- Node('/f/b/d/e') +-- Node('/f/g') +-- Node('/f/g/i') +-- Node('/f/g/i/h') Create a walker: >>> w = Walker() This class is made for walking: >>> w.walk(f, f) ((), Node('/f'), ()) >>> w.walk(f, b) ((), Node('/f'), (Node('/f/b'),)) >>> w.walk(b, f) ((Node('/f/b'),), Node('/f'), ()) >>> w.walk(h, e) ((Node('/f/g/i/h'), Node('/f/g/i'), Node('/f/g')), Node('/f'), (Node('/f/b'), Node('/f/b/d'), Node('/f/b/d/e'))) >>> w.walk(d, e) ((), Node('/f/b/d'), (Node('/f/b/d/e'),)) For a proper walking the nodes need to be part of the same tree: >>> w.walk(Node("a"), Node("b")) Traceback (most recent call last): ... anytree.walker.WalkError: Node('/a') and Node('/b') are not part of the same tree. """ startpath = start.path endpath = end.path if start.root is not end.root: msg = f"{start!r} and {end!r} are not part of the same tree." raise WalkError(msg) # common common = Walker.__calc_common(startpath, endpath) if ASSERTIONS: # pragma: no branch assert common[0] is start.root len_common = len(common) # upwards if start is common[-1]: upwards = () else: upwards = tuple(reversed(startpath[len_common:])) # down if end is common[-1]: down = () else: down = endpath[len_common:] return upwards, common[-1], down @staticmethod def __calc_common(start, end): return tuple(si for si, ei in zip(start, end) if si is ei) class WalkError(RuntimeError): """Walk Error.""" anytree-2.13.0/tests/000077500000000000000000000000001500063244000144005ustar00rootroot00000000000000anytree-2.13.0/tests/__init__.py000066400000000000000000000000151500063244000165050ustar00rootroot00000000000000"""Tests.""" anytree-2.13.0/tests/helper.py000066400000000000000000000010371500063244000162320ustar00rootroot00000000000000"""Helper Methods for testing.""" from contextlib import contextmanager def eq_(one, other): assert one == other, f"{one} != {other}" # hack own assert_raises, because py26 has a different implementation @contextmanager def assert_raises(exccls, msg): """Check exception of class `exccls` to be raised with message `msg`.""" try: yield raise AssertionError(f"{exccls!r} not raised") except Exception as exc: assert isinstance(exc, exccls), f"{exc!r} is not a {exccls!r}" eq_(str(exc), msg) anytree-2.13.0/tests/refdata/000077500000000000000000000000001500063244000160065ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexport/000077500000000000000000000000001500063244000222365ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexport/test_tree1/000077500000000000000000000000001500063244000243155ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexport/test_tree1/tree1.dot000066400000000000000000000004641500063244000260510ustar00rootroot00000000000000digraph tree { "root"; "sub0"; "sub0B"; "sub0A"; "sub1"; "sub1A"; "sub1B"; "sub1C"; "99"; "root" -> "sub0"; "root" -> "sub1"; "sub0" -> "sub0B"; "sub0" -> "sub0A"; "sub1" -> "sub1A"; "sub1" -> "sub1B"; "sub1" -> "sub1C"; "sub1C" -> "99"; } anytree-2.13.0/tests/refdata/tests.test_dotexport/test_tree2/000077500000000000000000000000001500063244000243165ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexport/test_tree2/tree2.dot000066400000000000000000000012221500063244000260440ustar00rootroot00000000000000digraph tree { rankdir=LR; "root:0" [shape=box]; "sub0:1" [shape=box]; "sub0B:2" [shape=box]; "sub0A:2" [shape=box]; "sub1:1" [shape=box]; "sub1A:2" [shape=box]; "sub1B:2" [shape=box]; "sub1C:2" [shape=box]; "sub1Ca:3" [shape=box]; "root:0" -> "sub0:1" [label="root:sub0"]; "root:0" -> "sub1:1" [label="root:sub1"]; "sub0:1" -> "sub0B:2" [label="sub0:sub0B"]; "sub0:1" -> "sub0A:2" [label="sub0:sub0A"]; "sub1:1" -> "sub1A:2" [label="sub1:sub1A"]; "sub1:1" -> "sub1B:2" [label="sub1:sub1B"]; "sub1:1" -> "sub1C:2" [label="sub1:sub1C"]; "sub1C:2" -> "sub1Ca:3" [label="sub1C:sub1Ca"]; } anytree-2.13.0/tests/refdata/tests.test_dotexporter/000077500000000000000000000000001500063244000225655ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree/000077500000000000000000000000001500063244000245635ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree/tree.dot000066400000000000000000000005061500063244000262330ustar00rootroot00000000000000digraph tree { "root"; "sub0"; "sub0B"; "sub0A"; "sub1"; "sub1A"; "sub1\"B"; "su\\b1C"; "sub1Ca"; "root" -> "sub0"; "root" -> "sub1"; "sub0" -> "sub0B"; "sub0" -> "sub0A"; "sub1" -> "sub1A"; "sub1" -> "sub1\"B"; "sub1" -> "su\\b1C"; "su\\b1C" -> "sub1Ca"; } anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_custom/000077500000000000000000000000001500063244000261555ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_custom/tree_custom.dot000066400000000000000000000012371500063244000312210ustar00rootroot00000000000000digraph tree { rankdir=LR; "root:0" [shape=box]; "sub0:1" [shape=box]; "sub0B:2" [shape=box]; "sub0A:2" [shape=box]; "sub1:1" [shape=box]; "sub1A:2" [shape=box]; "sub1\"B:2" [shape=box]; "su\\b1C:2" [shape=box]; "sub1Ca:3" [shape=box]; "root:0" -> "sub0:1" [label="root:sub0"]; "root:0" -> "sub1:1" [label="root:sub1"]; "sub0:1" -> "sub0B:2" [label="sub0:sub0B"]; "sub0:1" -> "sub0A:2" [label="sub0:sub0A"]; "sub1:1" -> "sub1A:2" [label="sub1:sub1A"]; "sub1:1" -> "sub1\"B:2" [label="sub1:sub1"B"]; "sub1:1" -> "su\\b1C:2" [label="sub1:su\b1C"]; "su\\b1C:2" -> "sub1Ca:3" [label="su\b1C:sub1Ca"]; } anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_filter/000077500000000000000000000000001500063244000261305ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_filter/tree_filter.dot000066400000000000000000000003131500063244000311410ustar00rootroot00000000000000digraph tree { "sub0"; "sub0B"; "sub0A"; "sub1"; "sub1A"; "sub1\"B"; "sub1Ca"; "sub0" -> "sub0B"; "sub0" -> "sub0A"; "sub1" -> "sub1A"; "sub1" -> "sub1\"B"; } anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_maxlevel/000077500000000000000000000000001500063244000264605ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_maxlevel/tree_maxlevel.dot000066400000000000000000000001411500063244000320200ustar00rootroot00000000000000digraph tree { "root"; "sub0"; "sub1"; "root" -> "sub0"; "root" -> "sub1"; } anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_stop/000077500000000000000000000000001500063244000256305ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_dotexporter/test_tree_stop/tree_stop.dot000066400000000000000000000002351500063244000303440ustar00rootroot00000000000000digraph tree { "root"; "sub0"; "sub0B"; "sub0A"; "root" -> "sub0"; "root" -> "sub1"; "sub0" -> "sub0B"; "sub0" -> "sub0A"; } anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/000077500000000000000000000000001500063244000234155ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree/000077500000000000000000000000001500063244000254135ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree/tree.md000066400000000000000000000003051500063244000266720ustar00rootroot00000000000000```mermaid graph TD N0["root"] N1["sub0"] N2["sub0B"] N3["sub0A"] N4["sub1"] N5["sub1A"] N6["sub1\"B"] N7["su\\b1C"] N8["sub1Ca"] N0-->N1 N0-->N4 N1-->N2 N1-->N3 N4-->N5 N4-->N6 N4-->N7 N7-->N8 ```anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_custom/000077500000000000000000000000001500063244000270055ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_custom/tree_custom.md000066400000000000000000000004221500063244000316560ustar00rootroot00000000000000```mermaid graph TD %% just an example comment %% could be an option too N0("root") N1("sub0") N2("sub0B") N3("sub0A") N4("sub1") N5("sub1A") N6("sub1"B") N7("su\b1C") N8("sub1Ca") N0--2-->N1 N0---->N4 N1--109-->N2 N1---->N3 N4--7-->N5 N4--8-->N6 N4--22-->N7 N7--42-->N8 ```anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_filter/000077500000000000000000000000001500063244000267605ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_filter/tree_filter.md000066400000000000000000000002141500063244000316030ustar00rootroot00000000000000```mermaid graph TD N0["sub0"] N1["sub0B"] N2["sub0A"] N3["sub1"] N4["sub1A"] N5["sub1\"B"] N6["sub1Ca"] N0-->N1 N0-->N2 N3-->N4 N3-->N5 ```anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_maxlevel/000077500000000000000000000000001500063244000273105ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_maxlevel/tree_maxlevel.md000066400000000000000000000001101500063244000324560ustar00rootroot00000000000000```mermaid graph TD N0["root"] N1["sub0"] N2["sub1"] N0-->N1 N0-->N2 ```anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_stop/000077500000000000000000000000001500063244000264605ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_mermaidexporter/test_tree_stop/tree_stop.md000066400000000000000000000001351500063244000310050ustar00rootroot00000000000000```mermaid graph TD N0["root"] N1["sub0"] N2["sub0B"] N3["sub0A"] N0-->N1 N1-->N2 N1-->N3 ```anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/000077500000000000000000000000001500063244000240145ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree/000077500000000000000000000000001500063244000260125ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree/tree.dot000066400000000000000000000006441500063244000274650ustar00rootroot00000000000000digraph tree { "0x0" [label="root"]; "0x1" [label="sub0"]; "0x2" [label="sub0B"]; "0x3" [label="sub0A"]; "0x4" [label="sub1"]; "0x5" [label="sub1A"]; "0x6" [label="sub1"B"]; "0x7" [label="su\b1C"]; "0x8" [label="sub1Ca"]; "0x0" -> "0x1"; "0x0" -> "0x4"; "0x1" -> "0x2"; "0x1" -> "0x3"; "0x4" -> "0x5"; "0x4" -> "0x6"; "0x4" -> "0x7"; "0x7" -> "0x8"; } anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_custom/000077500000000000000000000000001500063244000274045ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_custom/tree_custom.dot000066400000000000000000000012371500063244000324500ustar00rootroot00000000000000digraph tree { rankdir=LR; "root:0" [shape=box]; "sub0:1" [shape=box]; "sub0B:2" [shape=box]; "sub0A:2" [shape=box]; "sub1:1" [shape=box]; "sub1A:2" [shape=box]; "sub1\"B:2" [shape=box]; "su\\b1C:2" [shape=box]; "sub1Ca:3" [shape=box]; "root:0" -> "sub0:1" [label="root:sub0"]; "root:0" -> "sub1:1" [label="root:sub1"]; "sub0:1" -> "sub0B:2" [label="sub0:sub0B"]; "sub0:1" -> "sub0A:2" [label="sub0:sub0A"]; "sub1:1" -> "sub1A:2" [label="sub1:sub1A"]; "sub1:1" -> "sub1\"B:2" [label="sub1:sub1"B"]; "sub1:1" -> "su\\b1C:2" [label="sub1:su\b1C"]; "su\\b1C:2" -> "sub1Ca:3" [label="su\b1C:sub1Ca"]; } anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_filter/000077500000000000000000000000001500063244000273575ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_filter/tree_filter.dot000066400000000000000000000004361500063244000323760ustar00rootroot00000000000000digraph tree { "0x0" [label="sub0"]; "0x1" [label="sub0B"]; "0x2" [label="sub0A"]; "0x3" [label="sub1"]; "0x4" [label="sub1A"]; "0x5" [label="sub1"B"]; "0x6" [label="sub1Ca"]; "0x0" -> "0x1"; "0x0" -> "0x2"; "0x3" -> "0x4"; "0x3" -> "0x5"; } anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_maxlevel/000077500000000000000000000000001500063244000277075ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_maxlevel/tree_maxlevel.dot000066400000000000000000000002071500063244000332520ustar00rootroot00000000000000digraph tree { "0x0" [label="root"]; "0x1" [label="sub0"]; "0x2" [label="sub1"]; "0x0" -> "0x1"; "0x0" -> "0x2"; } anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_stop/000077500000000000000000000000001500063244000270575ustar00rootroot00000000000000anytree-2.13.0/tests/refdata/tests.test_uniquedotexporter/test_tree_stop/tree_stop.dot000066400000000000000000000003131500063244000315700ustar00rootroot00000000000000digraph tree { "0x0" [label="root"]; "0x1" [label="sub0"]; "0x2" [label="sub0B"]; "0x3" [label="sub0A"]; "0x0" -> "0x1"; "0x0" -> "0x4"; "0x1" -> "0x2"; "0x1" -> "0x3"; } anytree-2.13.0/tests/test_cachedsearch.py000066400000000000000000000043621500063244000204130ustar00rootroot00000000000000from anytree import CountError, Node from anytree.cachedsearch import find, find_by_attr, findall, findall_by_attr from .helper import assert_raises, eq_ def test_findall(): f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) eq_(findall(f, filter_=lambda node: node.name in ("a", "b")), (b, a)) eq_(findall(f, filter_=lambda node: d in node.path), (d, c, e)) with assert_raises( CountError, ("Expecting at least 4 elements, but found 3. (Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e'))"), ): findall(f, filter_=lambda node: d in node.path, mincount=4) with assert_raises( CountError, ("Expecting 2 elements at maximum, but found 3. (Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e'))"), ): findall(f, filter_=lambda node: d in node.path, maxcount=2) def test_findall_by_attr(): f = Node("f") b = Node("b", parent=f) Node("a", parent=b) d = Node("d", parent=b) Node("c", parent=d) Node("e", parent=d) eq_(findall_by_attr(f, "d"), (d,)) with assert_raises(CountError, ("Expecting at least 1 elements, but found 0.")): findall_by_attr(f, "z", mincount=1) def test_find(): f = Node("f") b = Node("b", parent=f) Node("a", parent=b) d = Node("d", parent=b) Node("c", parent=d) Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) Node("h", parent=i) eq_(find(f, lambda n: n.name == "d"), d) eq_(find(f, lambda n: n.name == "z"), None) with assert_raises( CountError, ( "Expecting 1 elements at maximum, but found 5. " "(Node('/f/b'), Node('/f/b/a'), Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e'))" ), ): find(f, lambda n: b in n.path) def test_find_by_attr(): f = Node("f") b = Node("b", parent=f) Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d, foo=4) Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) Node("h", parent=i) eq_(find_by_attr(f, "d"), d) eq_(find_by_attr(f, name="foo", value=4), c) eq_(find_by_attr(f, name="foo", value=8), None) anytree-2.13.0/tests/test_dictexporter.py000066400000000000000000000075201500063244000205310ustar00rootroot00000000000000from anytree import AnyNode, Node, NodeMixin from anytree.exporter import DictExporter from .helper import eq_ def test_dict_exporter(): """Dict Exporter.""" root = AnyNode(id="root") s0 = AnyNode(id="sub0", parent=root) s0b = AnyNode(id="sub0B", parent=s0) s0a = AnyNode(id="sub0A", parent=s0) s1 = AnyNode(id="sub1", parent=root, foo="bar") s1a = AnyNode(id="sub1A", parent=s1) s1b = AnyNode(id="sub1B", parent=s1) s1c = AnyNode(id="sub1C", parent=s1) s1ca = AnyNode(id="sub1Ca", parent=s1c) exporter = DictExporter() eq_( exporter.export(root), { "id": "root", "children": [ {"id": "sub0", "children": [{"id": "sub0B"}, {"id": "sub0A"}]}, { "id": "sub1", "foo": "bar", "children": [{"id": "sub1A"}, {"id": "sub1B"}, {"id": "sub1C", "children": [{"id": "sub1Ca"}]}], }, ], }, ) def test_dict_exporter_node(): """Dict Exporter.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root, foo="bar") s1a = Node("sub1A", parent=s1) s1b = Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) exporter = DictExporter() eq_( exporter.export(root), { "name": "root", "children": [ {"name": "sub0", "children": [{"name": "sub0B"}, {"name": "sub0A"}]}, { "name": "sub1", "foo": "bar", "children": [ {"name": "sub1A"}, {"name": "sub1B"}, {"name": "sub1C", "children": [{"name": "sub1Ca"}]}, ], }, ], }, ) def test_dict_exporter_filter(): """Dict Exporter.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root, foo="bar") s1a = Node("sub1A", parent=s1) s1b = Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) exporter = DictExporter(attriter=lambda attrs: [(k, v) for k, v in attrs if k == "name"]) eq_( exporter.export(root), { "name": "root", "children": [ {"name": "sub0", "children": [{"name": "sub0B"}, {"name": "sub0A"}]}, { "name": "sub1", "children": [ {"name": "sub1A"}, {"name": "sub1B"}, {"name": "sub1C", "children": [{"name": "sub1Ca"}]}, ], }, ], }, ) def test_dict_exporter_mixin(): """Dict Exporter.""" class MyClass(NodeMixin): def __init__(self, foo, parent=None): super().__init__() self.foo = foo self.parent = parent root = MyClass("root") s0 = MyClass("s0", parent=root) s0b = MyClass("s0b", parent=s0) s0a = MyClass("s0a", parent=s0) s1 = MyClass("s1", parent=root) s1a = MyClass("s1a", parent=s1) s1b = MyClass("s1b", parent=s1) s1c = MyClass("s1c", parent=s1) s1ca = MyClass("s1ca", parent=s1c) exporter = DictExporter() eq_( exporter.export(root), { "foo": "root", "children": [ {"foo": "s0", "children": [{"foo": "s0b"}, {"foo": "s0a"}]}, { "foo": "s1", "children": [{"foo": "s1a"}, {"foo": "s1b"}, {"foo": "s1c", "children": [{"foo": "s1ca"}]}], }, ], }, ) anytree-2.13.0/tests/test_dictimporter.py000066400000000000000000000044501500063244000205210ustar00rootroot00000000000000from copy import deepcopy from anytree import Node, RenderTree from anytree.exporter import DictExporter from anytree.importer import DictImporter from .helper import eq_ def test_dict_importer(): """Dict Importer.""" importer = DictImporter() exporter = DictExporter() refdata = { "id": "root", "children": [ {"id": "sub0", "children": [{"id": "sub0B"}, {"id": "sub0A"}]}, { "id": "sub1", "children": [{"id": "sub1A"}, {"id": "sub1B"}, {"id": "sub1C", "children": [{"id": "sub1Ca"}]}], }, ], } data = deepcopy(refdata) root = importer.import_(data) eq_(data, refdata) eq_(exporter.export(root), data) r = RenderTree(root) assert str(r).splitlines() == [ "AnyNode(id='root')", "├── AnyNode(id='sub0')", "│ ├── AnyNode(id='sub0B')", "│ └── AnyNode(id='sub0A')", "└── AnyNode(id='sub1')", " ├── AnyNode(id='sub1A')", " ├── AnyNode(id='sub1B')", " └── AnyNode(id='sub1C')", " └── AnyNode(id='sub1Ca')", ] def test_dict_importer_node(): """Dict Importer.""" importer = DictImporter(Node) exporter = DictExporter() refdata = { "name": "root", "children": [ {"name": "sub0", "children": [{"name": "sub0B"}, {"name": "sub0A"}]}, { "name": "sub1", "children": [ {"name": "sub1A"}, {"name": "sub1B"}, {"name": "sub1C", "children": [{"name": "sub1Ca"}]}, ], }, ], } data = deepcopy(refdata) root = importer.import_(data) eq_(data, refdata) eq_(exporter.export(root), data) r = RenderTree(root) assert str(r).splitlines() == [ "Node('/root')", "├── Node('/root/sub0')", "│ ├── Node('/root/sub0/sub0B')", "│ └── Node('/root/sub0/sub0A')", "└── Node('/root/sub1')", " ├── Node('/root/sub1/sub1A')", " ├── Node('/root/sub1/sub1B')", " └── Node('/root/sub1/sub1C')", " └── Node('/root/sub1/sub1C/sub1Ca')", ] anytree-2.13.0/tests/test_dotexport.py000066400000000000000000000035331500063244000200450ustar00rootroot00000000000000from shutil import which from pytest import mark from test2ref import assert_refdata from anytree import Node from anytree.dotexport import RenderTreeGraph def test_tree1(tmp_path): """Tree1.""" root = Node("root") s0 = Node("sub0", parent=root) Node("sub0B", parent=s0) Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) Node("sub1A", parent=s1) Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) Node(99, parent=s1c) RenderTreeGraph(root).to_dotfile(tmp_path / "tree1.dot") assert_refdata(test_tree1, tmp_path) def test_tree2(tmp_path): """Tree2.""" root = Node("root") s0 = Node("sub0", parent=root, edge=2) Node("sub0B", parent=s0, foo=4, edge=109) Node("sub0A", parent=s0, edge="") s1 = Node("sub1", parent=root, edge="") Node("sub1A", parent=s1, edge=7) Node("sub1B", parent=s1, edge=8) s1c = Node("sub1C", parent=s1, edge=22) Node("sub1Ca", parent=s1c, edge=42) def nodenamefunc(node): return f"{node.name}:{node.depth}" def edgeattrfunc(node, child): return f'label="{node.name}:{child.name}"' r = RenderTreeGraph( root, options=["rankdir=LR;"], nodenamefunc=nodenamefunc, nodeattrfunc=lambda node: "shape=box", edgeattrfunc=edgeattrfunc, ) r.to_dotfile(tmp_path / "tree2.dot") assert_refdata(test_tree2, tmp_path) @mark.skipif(which("dot") is None, reason="requires graphviz`s `dot` command") def test_tree_png(tmp_path): """Tree to png.""" root = Node("root") s0 = Node("sub0", parent=root) Node("sub0B", parent=s0) Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) Node("sub1A", parent=s1) Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) Node("sub1Ca", parent=s1c) RenderTreeGraph(root).to_picture(tmp_path / "tree1.png") anytree-2.13.0/tests/test_dotexporter.py000066400000000000000000000041151500063244000203710ustar00rootroot00000000000000from pytest import fixture from test2ref import assert_refdata from anytree import Node from anytree.exporter import DotExporter @fixture def root(): root = Node("root") s0 = Node("sub0", parent=root, edge=2) Node("sub0B", parent=s0, foo=4, edge=109) Node("sub0A", parent=s0, edge="") s1 = Node("sub1", parent=root, edge="") Node("sub1A", parent=s1, edge=7) Node('sub1"B', parent=s1, edge=8) s1c = Node("su\\b1C", parent=s1, edge=22) Node("sub1Ca", parent=s1c, edge=42) yield root def test_tree(tmp_path, root): """Tree.""" DotExporter(root).to_dotfile(tmp_path / "tree.dot") assert_refdata(test_tree, tmp_path) def test_tree_custom(tmp_path, root): """Tree Custom.""" def nodenamefunc(node): return f"{node.name}:{node.depth}" def edgeattrfunc(node, child): return f'label="{node.name}:{child.name}"' def nodefunc(node): return f'("{node.name}")' def edgefunc(node, child): return f"--{child.edge}-->" DotExporter( root, options=["rankdir=LR;"], nodenamefunc=nodenamefunc, nodeattrfunc=lambda node: "shape=box", edgeattrfunc=edgeattrfunc, ).to_dotfile(tmp_path / "tree_custom.dot") assert_refdata(test_tree_custom, tmp_path) def test_tree_filter(tmp_path, root): """Tree with Filter.""" DotExporter(root, filter_=lambda node: node.name.startswith("sub")).to_dotfile(tmp_path / "tree_filter.dot") assert_refdata(test_tree_filter, tmp_path) def test_tree_stop(tmp_path, root): """Tree with stop.""" DotExporter(root, stop=lambda node: node.name == "sub1").to_dotfile(tmp_path / "tree_stop.dot") assert_refdata(test_tree_stop, tmp_path) def test_tree_maxlevel(tmp_path, root): """Tree with maxlevel.""" DotExporter(root, maxlevel=2).to_dotfile(tmp_path / "tree_maxlevel.dot") assert_refdata(test_tree_maxlevel, tmp_path) def test_esc(): """Test proper escape of quotes.""" n = Node(r'6"-6\"') assert tuple(DotExporter(n)) == ( r"digraph tree {", r' "6\"-6\\\"";', r"}", ) anytree-2.13.0/tests/test_examples.py000066400000000000000000000015021500063244000176250ustar00rootroot00000000000000from anytree import Node, RenderTree from .helper import eq_ def test_stackoverflow(): """Example from stackoverflow.""" udo = Node("Udo") marc = Node("Marc", parent=udo) Node("Lian", parent=marc) dan = Node("Dan", parent=udo) Node("Jet", parent=dan) Node("Jan", parent=dan) joe = Node("Joe", parent=dan) eq_(str(udo), "Node('/Udo')") eq_(str(joe), "Node('/Udo/Dan/Joe')") eq_( [f"{pre}{node.name}" for pre, fill, node in RenderTree(udo)], [ "Udo", "├── Marc", "│ └── Lian", "└── Dan", " ├── Jet", " ├── Jan", " └── Joe", ], ) eq_(str(dan.children), "(Node('/Udo/Dan/Jet'), Node('/Udo/Dan/Jan'), Node('/Udo/Dan/Joe'))") anytree-2.13.0/tests/test_iterators.py000066400000000000000000000115521500063244000200310ustar00rootroot00000000000000from anytree import ( LevelGroupOrderIter, LevelOrderGroupIter, LevelOrderIter, Node, PostOrderIter, PreOrderIter, ZigZagGroupIter, ) from .helper import eq_ def test_preorder(): """PreOrderIter.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) eq_(list(PreOrderIter(f)), [f, b, a, d, c, e, g, i, h]) eq_(list(PreOrderIter(f, maxlevel=0)), []) eq_(list(PreOrderIter(f, maxlevel=3)), [f, b, a, d, g, i]) eq_(list(PreOrderIter(f, filter_=lambda n: n.name not in ("e", "g"))), [f, b, a, d, c, i, h]) eq_(list(PreOrderIter(f, stop=lambda n: n.name == "d")), [f, b, a, g, i, h]) it = PreOrderIter(f) eq_(next(it), f) eq_(next(it), b) eq_(list(it), [a, d, c, e, g, i, h]) def test_postorder(): """PostOrderIter.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) eq_(list(PostOrderIter(f)), [a, c, e, d, b, h, i, g, f]) eq_(list(PostOrderIter(f, maxlevel=0)), []) eq_(list(PostOrderIter(f, maxlevel=3)), [a, d, b, i, g, f]) eq_(list(PostOrderIter(f, filter_=lambda n: n.name not in ("e", "g"))), [a, c, d, b, h, i, f]) eq_(list(PostOrderIter(f, stop=lambda n: n.name == "d")), [a, b, h, i, g, f]) it = PostOrderIter(f) eq_(next(it), a) eq_(next(it), c) eq_(list(it), [e, d, b, h, i, g, f]) def test_levelorder(): """LevelOrderIter.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) eq_(list(LevelOrderIter(f)), [f, b, g, a, d, i, c, e, h]) eq_(list(LevelOrderIter(f, maxlevel=0)), []) eq_(list(LevelOrderIter(f, maxlevel=3)), [f, b, g, a, d, i]) eq_(list(LevelOrderIter(f, filter_=lambda n: n.name not in ("e", "g"))), [f, b, a, d, i, c, h]) eq_(list(LevelOrderIter(f, stop=lambda n: n.name == "d")), [f, b, g, a, i, h]) it = LevelOrderIter(f) eq_(next(it), f) eq_(next(it), b) eq_(list(it), [g, a, d, i, c, e, h]) def test_levelgrouporder(): """LevelGroupOrderIter.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) eq_(list(LevelGroupOrderIter(f)), [(f,), (b, g), (a, d, i), (c, e, h)]) eq_(list(LevelGroupOrderIter(f, maxlevel=0)), []) eq_(list(LevelGroupOrderIter(f, maxlevel=3)), [(f,), (b, g), (a, d, i)]) eq_(list(LevelGroupOrderIter(f, filter_=lambda n: n.name not in ("e", "g"))), [(f,), (b,), (a, d, i), (c, h)]) eq_(list(LevelGroupOrderIter(f, stop=lambda n: n.name == "d")), [(f,), (b, g), (a, i), (h,)]) it = LevelGroupOrderIter(f) eq_(next(it), (f,)) eq_(next(it), (b, g)) eq_(list(it), [(a, d, i), (c, e, h)]) def test_levelordergroup(): """LevelOrderGroupIter.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) eq_(list(LevelOrderGroupIter(f)), [(f,), (b, g), (a, d, i), (c, e, h)]) eq_(list(LevelOrderGroupIter(f, maxlevel=0)), []) eq_(list(LevelOrderGroupIter(f, maxlevel=3)), [(f,), (b, g), (a, d, i)]) eq_(list(LevelOrderGroupIter(f, filter_=lambda n: n.name not in ("e", "g"))), [(f,), (b,), (a, d, i), (c, h)]) eq_(list(LevelOrderGroupIter(f, stop=lambda n: n.name == "d")), [(f,), (b, g), (a, i), (h,)]) it = LevelOrderGroupIter(f) eq_(next(it), (f,)) eq_(next(it), (b, g)) eq_(list(it), [(a, d, i), (c, e, h)]) def test_zigzaggroup(): """ZigZagGroupIter.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) eq_(list(ZigZagGroupIter(f)), [(f,), (g, b), (a, d, i), (h, e, c)]) eq_(list(ZigZagGroupIter(f, maxlevel=0)), []) eq_(list(ZigZagGroupIter(f, maxlevel=3)), [(f,), (g, b), (a, d, i)]) eq_(list(ZigZagGroupIter(f, filter_=lambda n: n.name not in ("e", "g"))), [(f,), (b,), (a, d, i), (h, c)]) eq_(list(ZigZagGroupIter(f, stop=lambda n: n.name == "d")), [(f,), (g, b), (a, i), (h,)]) it = ZigZagGroupIter(f) eq_(next(it), (f,)) eq_(next(it), (g, b)) eq_(list(it), [(a, d, i), (h, e, c)]) anytree-2.13.0/tests/test_jsonexporter.py000066400000000000000000000047541500063244000205650ustar00rootroot00000000000000import filecmp import os from tempfile import NamedTemporaryFile from anytree import AnyNode from anytree.exporter import JsonExporter from .helper import eq_ def test_json_exporter(): """Json Exporter.""" root = AnyNode(id="root") s0 = AnyNode(id="sub0", parent=root) AnyNode(id="sub0B", parent=s0) AnyNode(id="sub0A", parent=s0) s1 = AnyNode(id="sub1", parent=root) AnyNode(id="sub1A", parent=s1) AnyNode(id="sub1B", parent=s1) s1c = AnyNode(id="sub1C", parent=s1) AnyNode(id="sub1Ca", parent=s1c) exporter = JsonExporter(indent=2, sort_keys=True) exported = exporter.export(root).splitlines() exported = [e.rstrip() for e in exported] # just a fix for a strange py2x behavior. lines = [ "{", ' "children": [', " {", ' "children": [', " {", ' "id": "sub0B"', " },", " {", ' "id": "sub0A"', " }", " ],", ' "id": "sub0"', " },", " {", ' "children": [', " {", ' "id": "sub1A"', " },", " {", ' "id": "sub1B"', " },", " {", ' "children": [', " {", ' "id": "sub1Ca"', " }", " ],", ' "id": "sub1C"', " }", " ],", ' "id": "sub1"', " }", " ],", ' "id": "root"', "}", ] eq_(exported, lines) exporter = JsonExporter(indent=2, sort_keys=True, maxlevel=2) exported = exporter.export(root).splitlines() exported = [e.rstrip() for e in exported] # just a fix for a strange py2x behavior. limitedlines = [ "{", ' "children": [', " {", ' "id": "sub0"', " },", " {", ' "id": "sub1"', " }", " ],", ' "id": "root"', "}", ] eq_(exported, limitedlines) try: with NamedTemporaryFile(mode="w+", delete=False) as ref: with NamedTemporaryFile(mode="w+", delete=False) as gen: ref.write("\n".join(lines)) exporter.write(root, gen) # on Windows, you must close the files before comparison filecmp.cmp(ref.name, gen.name) finally: os.remove(ref.name) os.remove(gen.name) anytree-2.13.0/tests/test_jsonimporter.py000066400000000000000000000032631500063244000205500ustar00rootroot00000000000000from tempfile import NamedTemporaryFile from anytree.exporter import DictExporter from anytree.importer import JsonImporter from .helper import eq_ def test_json_importer(): """Json Importer.""" refdata = { "id": "root", "children": [ {"id": "sub0", "children": [{"id": "sub0B"}, {"id": "sub0A"}]}, { "id": "sub1", "children": [{"id": "sub1A"}, {"id": "sub1B"}, {"id": "sub1C", "children": [{"id": "sub1Ca"}]}], }, ], } lines = [ "{", ' "children": [', " {", ' "children": [', " {", ' "id": "sub0B"', " },", " {", ' "id": "sub0A"', " }", " ],", ' "id": "sub0"', " },", " {", ' "children": [', " {", ' "id": "sub1A"', " },", " {", ' "id": "sub1B"', " },", " {", ' "children": [', " {", ' "id": "sub1Ca"', " }", " ],", ' "id": "sub1C"', " }", " ],", ' "id": "sub1"', " }", " ],", ' "id": "root"', "}", ] imported = DictExporter().export(JsonImporter().import_("\n".join(lines))) eq_(refdata, imported) with NamedTemporaryFile(mode="w+") as ref: ref.write("\n".join(lines)) ref.seek(0) imported = DictExporter().export(JsonImporter().read(ref)) eq_(refdata, imported) anytree-2.13.0/tests/test_lightnode.py000066400000000000000000000337071500063244000200000ustar00rootroot00000000000000from anytree import LightNodeMixin, LoopError, PostOrderIter, PreOrderIter, TreeError from .helper import assert_raises class LightNode(LightNodeMixin): __slots__ = ["name"] def __init__(self, name, parent=None, children=None): self.name = name self.parent = parent if children: self.children = children def __repr__(self): path = self.separator.join([""] + [str(node.name) for node in self.path]) return f"{self.__class__.__name__}({path!r})" def test_parent_child(): """A tree parent and child attributes.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1a = LightNode("sub1A", parent=s1) s1b = LightNode("sub1B", parent=s1) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.parent is None assert root.children == (s0, s1) assert s0.parent == root assert s0.children == (s0b, s0a) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent == root assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == (s1ca,) assert s1ca.parent == s1c assert s1ca.children == () # change parent s1ca.parent = s0 assert root.parent is None assert root.children == (s0, s1) assert s0.parent == root assert s0.children == (s0b, s0a, s1ca) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent == root assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == () assert s1ca.parent == s0 assert s1ca.children == () # break tree into two s1.parent = None assert root.parent is None assert root.children == (s0,) assert s0.parent == root assert s0.children == (s0b, s0a, s1ca) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent is None assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == () assert s1ca.parent == s0 assert s1ca.children == () # set to the same s1b.parent = s1 assert root.parent is None assert root.children == (s0,) assert s0.parent == root assert s0.children == (s0b, s0a, s1ca) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent is None assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == () assert s1ca.parent == s0 assert s1ca.children == () def test_detach_children(): root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1a = LightNode("sub1A", parent=s1) s1b = LightNode("sub1B", parent=s1) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.descendants == (s0, s0b, s0a, s1, s1a, s1b, s1c, s1ca) del s0.children assert root.descendants == (s0, s1, s1a, s1b, s1c, s1ca) del s1.children assert root.descendants == (s0, s1) def test_children_setter(): root = LightNode("root") s0 = LightNode("sub0") s1 = LightNode("sub0A") s0a = LightNode("sub0B") root.children = [s0, s1] s0.children = [s0a] assert root.descendants == (s0, s0a, s1) with assert_raises(LoopError, "Cannot set parent. LightNode('/root/sub0') cannot be parent of itself."): s0.children = [s0] # test whether tree is unchanged after LoopError assert root.descendants == (s0, s0a, s1) with assert_raises( LoopError, "Cannot set parent. LightNode('/root/sub0') is parent of LightNode('/root/sub0/sub0B')." ): s0a.children = [s0] # test whether tree is unchanged after LoopError assert root.descendants == (s0, s0a, s1) root.children = [s0, s1] s0.children = [s0a] s0a.children = [s1] assert root.descendants == (s0, s0a, s1) def test_children_setter_large(): root = LightNode("root") s0 = LightNode("sub0") s0b = LightNode("sub0B") s0a = LightNode("sub0A") s1 = LightNode("sub1") s1a = LightNode("sub1A") s1b = LightNode("sub1B") s1c = LightNode("sub1C") s1ca = LightNode("sub1Ca") root.children = [s0, s1] assert root.descendants == (s0, s1) s0.children = [s0a, s0b] assert root.descendants == (s0, s0a, s0b, s1) s1.children = [s1a, s1b, s1c] assert root.descendants == (s0, s0a, s0b, s1, s1a, s1b, s1c) with assert_raises(TypeError, "'LightNode' object is not iterable"): s1.children = s1ca assert root.descendants == (s0, s0a, s0b, s1, s1a, s1b, s1c) def test_node_children_multiple(): root = LightNode("root") sub = LightNode("sub") with assert_raises(TreeError, "Cannot add node LightNode('/sub') multiple times as child."): root.children = [sub, sub] def test_recursion_detection(): """Recursion detection.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) # try recursion assert root.parent is None try: root.parent = root except LoopError as exc: assert str(exc) == "Cannot set parent. LightNode('/root') cannot be parent of itself." assert root.parent is None else: raise AssertionError assert root.parent is None try: root.parent = s0a except LoopError as exc: assert str(exc) == ("Cannot set parent. LightNode('/root') is parent of LightNode('/root/sub0/sub0A').") assert root.parent is None else: raise AssertionError assert s0.parent is root try: s0.parent = s0a except LoopError as exc: assert str(exc) == ("Cannot set parent. LightNode('/root/sub0') is parent of LightNode('/root/sub0/sub0A').") assert s0.parent is root else: raise AssertionError def test_ancestors(): """Node.ancestors.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.ancestors == () assert s0.ancestors == (root,) assert s0b.ancestors == (root, s0) assert s0a.ancestors == (root, s0) assert s1ca.ancestors == (root, s1, s1c) def test_node_children_init(): """Node With Children Attribute.""" root = LightNode("root", children=[LightNode("a", children=[LightNode("aa")]), LightNode("b")]) assert repr(root.descendants) == "(LightNode('/root/a'), LightNode('/root/a/aa'), LightNode('/root/b'))" def test_descendants(): """Node.descendants.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.descendants == (s0, s0b, s0a, s1, s1c, s1ca) assert s1.descendants == (s1c, s1ca) assert s1c.descendants == (s1ca,) assert s1ca.descendants == () def test_root(): """Node.root.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.root == root assert s0.root == root assert s0b.root == root assert s0a.root == root assert s1.root == root assert s1c.root == root assert s1ca.root == root def test_siblings(): """Node.siblings.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.siblings == () assert s0.siblings == (s1,) assert s0b.siblings == (s0a,) assert s0a.siblings == (s0b,) assert s1.siblings == (s0,) assert s1c.siblings == () assert s1ca.siblings == () def test_is_leaf(): """Node.is_leaf.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.is_leaf is False assert s0.is_leaf is False assert s0b.is_leaf is True assert s0a.is_leaf is True assert s1.is_leaf is False assert s1c.is_leaf is False assert s1ca.is_leaf is True def test_leaves(): """Node.leaves.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.leaves == (s0b, s0a, s1ca) assert s0.leaves == (s0b, s0a) assert s0b.leaves == (s0b,) assert s0a.leaves == (s0a,) assert s1.leaves == (s1ca,) assert s1c.leaves == (s1ca,) assert s1ca.leaves == (s1ca,) def test_is_root(): """Node.is_root.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.is_root is True assert s0.is_root is False assert s0b.is_root is False assert s0a.is_root is False assert s1.is_root is False assert s1c.is_root is False assert s1ca.is_root is False def test_height(): """Node.height.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.height == 3 assert s0.height == 1 assert s0b.height == 0 assert s0a.height == 0 assert s1.height == 2 assert s1c.height == 1 assert s1ca.height == 0 def test_size(): """Node.size.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.size == 7 assert s0.size == 3 assert s0b.size == 1 assert s0a.size == 1 assert s1.size == 3 assert s1c.size == 2 assert s1ca.size == 1 def test_depth(): """Node.depth.""" root = LightNode("root") s0 = LightNode("sub0", parent=root) s0b = LightNode("sub0B", parent=s0) s0a = LightNode("sub0A", parent=s0) s1 = LightNode("sub1", parent=root) s1c = LightNode("sub1C", parent=s1) s1ca = LightNode("sub1Ca", parent=s1c) assert root.depth == 0 assert s0.depth == 1 assert s0b.depth == 2 assert s0a.depth == 2 assert s1.depth == 1 assert s1c.depth == 2 assert s1ca.depth == 3 def test_parent(): """Parent attribute.""" foo = LightNodeMixin() assert foo.parent is None def test_pre_order_iter(): """Pre-Order Iterator.""" f = LightNode("f") b = LightNode("b", parent=f) a = LightNode("a", parent=b) d = LightNode("d", parent=b) c = LightNode("c", parent=d) e = LightNode("e", parent=d) g = LightNode("g", parent=f) i = LightNode("i", parent=g) h = LightNode("h", parent=i) assert [node.name for node in PreOrderIter(f)] == ["f", "b", "a", "d", "c", "e", "g", "i", "h"] def test_post_order_iter(): """Post-Order Iterator.""" f = LightNode("f") b = LightNode("b", parent=f) a = LightNode("a", parent=b) d = LightNode("d", parent=b) c = LightNode("c", parent=d) e = LightNode("e", parent=d) g = LightNode("g", parent=f) i = LightNode("i", parent=g) h = LightNode("h", parent=i) assert [node.name for node in PostOrderIter(f)] == ["a", "c", "e", "d", "b", "h", "i", "g", "f"] def test_hookups(): """Hookup attributes #29.""" class MyLightNode(LightNode): def _pre_attach(self, parent): assert str(self.parent) == "None" assert self.children == () assert str(self.path) == "(MyLightNode('/B'),)" def _post_attach(self, parent): assert str(self.parent) == "MyLightNode('/A')" assert self.children == () assert str(self.path) == "(MyLightNode('/A'), MyLightNode('/A/B'))" def _pre_detach(self, parent): assert str(self.parent) == "MyLightNode('/A')" assert self.children == () assert str(self.path) == "(MyLightNode('/A'), MyLightNode('/A/B'))" def _post_detach(self, parent): assert str(self.parent) == "None" assert self.children == () assert str(self.path) == "(MyLightNode('/B'),)" node_a = MyLightNode("A") node_b = MyLightNode("B", node_a) # attach B on A node_b.parent = None # detach B from A anytree-2.13.0/tests/test_mermaidexporter.py000066400000000000000000000034111500063244000212170ustar00rootroot00000000000000from pytest import fixture from test2ref import assert_refdata from anytree import Node from anytree.exporter import MermaidExporter @fixture def root(): root = Node("root") s0 = Node("sub0", parent=root, edge=2) Node("sub0B", parent=s0, foo=4, edge=109) Node("sub0A", parent=s0, edge="") s1 = Node("sub1", parent=root, edge="") Node("sub1A", parent=s1, edge=7) Node('sub1"B', parent=s1, edge=8) s1c = Node("su\\b1C", parent=s1, edge=22) Node("sub1Ca", parent=s1c, edge=42) yield root def test_tree(tmp_path, root): """Tree.""" MermaidExporter(root).to_file(tmp_path / "tree.md") assert_refdata(test_tree, tmp_path) def test_tree_custom(tmp_path, root): """Tree Custom.""" def nodefunc(node): return f'("{node.name}")' def edgefunc(node, child): return f"--{child.edge}-->" options = [ "%% just an example comment", "%% could be an option too", ] MermaidExporter( root, options=options, nodefunc=nodefunc, edgefunc=edgefunc, ).to_file(tmp_path / "tree_custom.md") assert_refdata(test_tree_custom, tmp_path) def test_tree_filter(tmp_path, root): """Tree with Filter.""" MermaidExporter(root, filter_=lambda node: node.name.startswith("sub")).to_file(tmp_path / "tree_filter.md") assert_refdata(test_tree_filter, tmp_path) def test_tree_stop(tmp_path, root): """Tree with stop.""" MermaidExporter(root, stop=lambda node: node.name == "sub1").to_file(tmp_path / "tree_stop.md") assert_refdata(test_tree_stop, tmp_path) def test_tree_maxlevel(tmp_path, root): """Tree with maxlevel.""" MermaidExporter(root, maxlevel=2).to_file(tmp_path / "tree_maxlevel.md") assert_refdata(test_tree_maxlevel, tmp_path) anytree-2.13.0/tests/test_node.py000066400000000000000000000377461500063244000167570ustar00rootroot00000000000000from anytree import AnyNode, LoopError, Node, NodeMixin, PostOrderIter, PreOrderIter, TreeError from .helper import assert_raises def test_node_parent_error(): """Node Parent Error.""" with assert_raises(TreeError, "Parent node 'parent' is not of type 'NodeMixin'."): Node("root", "parent") def test_parent_child(): """A tree parent and child attributes.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1a = Node("sub1A", parent=s1) s1b = Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.parent is None assert root.children == (s0, s1) assert s0.parent == root assert s0.children == (s0b, s0a) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent == root assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == (s1ca,) assert s1ca.parent == s1c assert s1ca.children == () # change parent s1ca.parent = s0 assert root.parent is None assert root.children == (s0, s1) assert s0.parent == root assert s0.children == (s0b, s0a, s1ca) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent == root assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == () assert s1ca.parent == s0 assert s1ca.children == () # break tree into two s1.parent = None assert root.parent is None assert root.children == (s0,) assert s0.parent == root assert s0.children == (s0b, s0a, s1ca) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent is None assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == () assert s1ca.parent == s0 assert s1ca.children == () # set to the same s1b.parent = s1 assert root.parent is None assert root.children == (s0,) assert s0.parent == root assert s0.children == (s0b, s0a, s1ca) assert s0b.parent == s0 assert s0b.children == () assert s0a.parent == s0 assert s0a.children == () assert s1.parent is None assert s1.children == (s1a, s1b, s1c) assert s1a.parent == s1 assert s1a.children == () assert s1b.parent == s1 assert s1b.children == () assert s1c.parent == s1 assert s1c.children == () assert s1ca.parent == s0 assert s1ca.children == () def test_detach_children(): root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1a = Node("sub1A", parent=s1) s1b = Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.descendants == (s0, s0b, s0a, s1, s1a, s1b, s1c, s1ca) del s0.children assert root.descendants == (s0, s1, s1a, s1b, s1c, s1ca) del s1.children assert root.descendants == (s0, s1) def test_children_setter(): root = Node("root") s0 = Node("sub0") s1 = Node("sub0A") s0a = Node("sub0B") root.children = [s0, s1] s0.children = [s0a] assert root.descendants == (s0, s0a, s1) with assert_raises(LoopError, "Cannot set parent. Node('/root/sub0') cannot be parent of itself."): s0.children = [s0] # test whether tree is unchanged after LoopError assert root.descendants == (s0, s0a, s1) with assert_raises(LoopError, "Cannot set parent. Node('/root/sub0') is parent of Node('/root/sub0/sub0B')."): s0a.children = [s0] # test whether tree is unchanged after LoopError assert root.descendants == (s0, s0a, s1) root.children = [s0, s1] s0.children = [s0a] s0a.children = [s1] assert root.descendants == (s0, s0a, s1) def test_children_setter_large(): root = Node("root") s0 = Node("sub0") s0b = Node("sub0B") s0a = Node("sub0A") s1 = Node("sub1") s1a = Node("sub1A") s1b = Node("sub1B") s1c = Node("sub1C") s1ca = Node("sub1Ca") root.children = [s0, s1] assert root.descendants == (s0, s1) s0.children = [s0a, s0b] assert root.descendants == (s0, s0a, s0b, s1) s1.children = [s1a, s1b, s1c] assert root.descendants == (s0, s0a, s0b, s1, s1a, s1b, s1c) with assert_raises(TypeError, "'Node' object is not iterable"): s1.children = s1ca assert root.descendants == (s0, s0a, s0b, s1, s1a, s1b, s1c) def test_node_children_type(): root = Node("root") with assert_raises(TreeError, "Cannot add non-node object 'string'. It is not a subclass of 'NodeMixin'."): root.children = ["string"] def test_node_children_multiple(): root = Node("root") sub = Node("sub") with assert_raises(TreeError, "Cannot add node Node('/sub') multiple times as child."): root.children = [sub, sub] def test_recursion_detection(): """Recursion detection.""" root = Node("root") s0 = Node("sub0", parent=root) Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) # try recursion assert root.parent is None try: root.parent = root except LoopError as exc: assert str(exc) == "Cannot set parent. Node('/root') cannot be parent of itself." assert root.parent is None else: raise AssertionError assert root.parent is None try: root.parent = s0a except LoopError as exc: assert str(exc) == ("Cannot set parent. Node('/root') is parent of Node('/root/sub0/sub0A').") assert root.parent is None else: raise AssertionError assert s0.parent is root try: s0.parent = s0a except LoopError as exc: assert str(exc) == ("Cannot set parent. Node('/root/sub0') is parent of Node('/root/sub0/sub0A').") assert s0.parent is root else: raise AssertionError def test_repr(): """Node representation.""" root = Node("root") s0 = Node("sub0", parent=root) s1 = Node("sub1", parent=root, foo=42, bar="c0fe") assert repr(root) == "Node('/root')" assert repr(s0) == "Node('/root/sub0')" assert repr(s1) == "Node('/root/sub1', bar='c0fe', foo=42)" def test_ancestors(): """Node.ancestors.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.ancestors == () assert s0.ancestors == (root,) assert s0b.ancestors == (root, s0) assert s0a.ancestors == (root, s0) assert s1ca.ancestors == (root, s1, s1c) # deprecated typo assert s1ca.ancestors == (root, s1, s1c) def test_node_children_init(): """Node With Children Attribute.""" root = Node("root", children=[Node("a", children=[Node("aa")]), Node("b")]) assert repr(root.descendants) == "(Node('/root/a'), Node('/root/a/aa'), Node('/root/b'))" def test_anynode_children_init(): """Anynode With Children Attribute.""" root = AnyNode(foo="root", children=[AnyNode(foo="a", children=[AnyNode(foo="aa")]), AnyNode(foo="b")]) assert repr(root.descendants) == "(AnyNode(foo='a'), AnyNode(foo='aa'), AnyNode(foo='b'))" def test_descendants(): """Node.descendants.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.descendants == (s0, s0b, s0a, s1, s1c, s1ca) assert s1.descendants == (s1c, s1ca) assert s1c.descendants == (s1ca,) assert s1ca.descendants == () def test_root(): """Node.root.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.root == root assert s0.root == root assert s0b.root == root assert s0a.root == root assert s1.root == root assert s1c.root == root assert s1ca.root == root def test_siblings(): """Node.siblings.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.siblings == () assert s0.siblings == (s1,) assert s0b.siblings == (s0a,) assert s0a.siblings == (s0b,) assert s1.siblings == (s0,) assert s1c.siblings == () assert s1ca.siblings == () def test_is_leaf(): """Node.is_leaf.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.is_leaf is False assert s0.is_leaf is False assert s0b.is_leaf is True assert s0a.is_leaf is True assert s1.is_leaf is False assert s1c.is_leaf is False assert s1ca.is_leaf is True def test_leaves(): """Node.leaves.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.leaves == (s0b, s0a, s1ca) assert s0.leaves == (s0b, s0a) assert s0b.leaves == (s0b,) assert s0a.leaves == (s0a,) assert s1.leaves == (s1ca,) assert s1c.leaves == (s1ca,) assert s1ca.leaves == (s1ca,) def test_is_root(): """Node.is_root.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.is_root is True assert s0.is_root is False assert s0b.is_root is False assert s0a.is_root is False assert s1.is_root is False assert s1c.is_root is False assert s1ca.is_root is False def test_height(): """Node.height.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.height == 3 assert s0.height == 1 assert s0b.height == 0 assert s0a.height == 0 assert s1.height == 2 assert s1c.height == 1 assert s1ca.height == 0 def test_size(): """Node.size.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.size == 7 assert s0.size == 3 assert s0b.size == 1 assert s0a.size == 1 assert s1.size == 3 assert s1c.size == 2 assert s1ca.size == 1 def test_depth(): """Node.depth.""" root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) assert root.depth == 0 assert s0.depth == 1 assert s0b.depth == 2 assert s0a.depth == 2 assert s1.depth == 1 assert s1c.depth == 2 assert s1ca.depth == 3 def test_parent(): """Parent attribute.""" foo = NodeMixin() assert foo.parent is None def test_pre_order_iter(): """Pre-Order Iterator.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) assert [node.name for node in PreOrderIter(f)] == ["f", "b", "a", "d", "c", "e", "g", "i", "h"] def test_post_order_iter(): """Post-Order Iterator.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) assert [node.name for node in PostOrderIter(f)] == ["a", "c", "e", "d", "b", "h", "i", "g", "f"] def test_anyname(): """Support any type as name.""" myroot = Node([1, 2, 3]) Node("/foo", parent=myroot) assert str(myroot) == "Node('/[1, 2, 3]')" def test_node_kwargs(): """Ticket #24.""" class MyNode(Node): def __init__(self, name, parent=None, **kwargs): super().__init__(name, parent, **kwargs) def _post_attach(self, parent): print(self.my_attribute) node_a = MyNode("A") node_b = MyNode("B", node_a, my_attribute=True) assert repr(node_b) == "MyNode('/A/B', my_attribute=True)" def test_hookups(): """Hookup attributes #29.""" class MyNode(Node): def _pre_attach(self, parent): assert str(self.parent) == "None" assert self.children == () assert str(self.path) == "(MyNode('/B'),)" def _post_attach(self, parent): assert str(self.parent) == "MyNode('/A')" assert self.children == () assert str(self.path) == "(MyNode('/A'), MyNode('/A/B'))" def _pre_detach(self, parent): assert str(self.parent) == "MyNode('/A')" assert self.children == () assert str(self.path) == "(MyNode('/A'), MyNode('/A/B'))" def _post_detach(self, parent): assert str(self.parent) == "None" assert self.children == () assert str(self.path) == "(MyNode('/B'),)" node_a = MyNode("A") node_b = MyNode("B", node_a) # attach B on A node_b.parent = None # detach B from A def test_any_node_parent_error(): """Any Node Parent Error.""" with assert_raises(TreeError, "Parent node 'r' is not of type 'NodeMixin'."): AnyNode("r") def test_any_node(): """Any Node.""" r = AnyNode() a = AnyNode() b = AnyNode(foo=4) assert r.parent is None assert a.parent is None assert b.parent is None a.parent = r b.parent = r assert r.children == (a, b) assert repr(r) == "AnyNode()" assert repr(a) == "AnyNode()" assert repr(b) == "AnyNode(foo=4)" def test_eq_overwrite(): """Node with overwritten __eq__.""" class EqOverwrittingNode(NodeMixin): def __init__(self, a, b, parent=None): super().__init__() self.a = a self.b = b self.parent = parent def __eq__(self, other): if isinstance(other, EqOverwrittingNode): return self.a == other.a and self.b == other.b return NotImplemented r = EqOverwrittingNode(0, 0) a = EqOverwrittingNode(1, 0, parent=r) b = EqOverwrittingNode(1, 0, parent=r) assert a.parent is r assert b.parent is r assert a.a == 1 assert a.b == 0 assert b.a == 1 assert b.b == 0 def test_tuple(): """Tuple as parent.""" with assert_raises(TreeError, "Parent node (1, 0, 3) is not of type 'NodeMixin'."): Node((0, 1, 2), parent=(1, 0, 3)) def test_tuple_as_children(): """Tuple as children.""" n = Node("foo") with assert_raises(TreeError, "Cannot add non-node object (0, 1, 2). It is not a subclass of 'NodeMixin'."): n.children = [(0, 1, 2)] anytree-2.13.0/tests/test_node_attach_detach.py000066400000000000000000000226301500063244000215750ustar00rootroot00000000000000from anytree import LoopError, Node from .helper import assert_raises class TNode(Node): TRACKING = [] # noqa: RUF012 def _pre_detach(self, parent): """Method call before detaching from `parent`.""" self.TRACKING.append(f"_pre_detach({self.name!r}, {parent.name!r})") def _post_detach(self, parent): """Method call after detaching from `parent`.""" self.TRACKING.append(f"_post_detach({self.name!r}, {parent.name!r})") def _pre_attach(self, parent): """Method call before attaching to `parent`.""" self.TRACKING.append(f"_pre_attach({self.name!r}, {parent.name!r})") def _post_attach(self, parent): """Method call after attaching to `parent`.""" self.TRACKING.append(f"_post_attach({self.name!r}, {parent.name!r})") def _pre_detach_children(self, children): """Method call before detaching `children`.""" self.TRACKING.append(f"_pre_detach_children({self.name!r}, {tuple(child.name for child in children)!r})") def _post_detach_children(self, children): """Method call after detaching `children`.""" self.TRACKING.append(f"_post_detach_children({self.name!r}, {tuple(child.name for child in children)!r})") def _pre_attach_children(self, children): """Method call before attaching `children`.""" self.TRACKING.append(f"_pre_attach_children({self.name!r}, {tuple(child.name for child in children)!r})") def _post_attach_children(self, children): """Method call after attaching `children`.""" self.TRACKING.append(f"_post_attach_children({self.name!r}, {tuple(child.name for child in children)!r})") def test_parent_child(): """A tree parent and child attributes.""" root = TNode("root") s0 = TNode("sub0", parent=root) s0b = TNode("sub0B", parent=s0) s0a = TNode("sub0A", parent=s0) s1 = TNode("sub1", parent=root) s1a = TNode("sub1A", parent=s1) s1b = TNode("sub1B", parent=s1) s1c = TNode("sub1C", parent=s1) s1ca = TNode("sub1Ca", parent=s1c) assert TNode.TRACKING == [ "_pre_attach('sub0', 'root')", "_post_attach('sub0', 'root')", "_pre_attach('sub0B', 'sub0')", "_post_attach('sub0B', 'sub0')", "_pre_attach('sub0A', 'sub0')", "_post_attach('sub0A', 'sub0')", "_pre_attach('sub1', 'root')", "_post_attach('sub1', 'root')", "_pre_attach('sub1A', 'sub1')", "_post_attach('sub1A', 'sub1')", "_pre_attach('sub1B', 'sub1')", "_post_attach('sub1B', 'sub1')", "_pre_attach('sub1C', 'sub1')", "_post_attach('sub1C', 'sub1')", "_pre_attach('sub1Ca', 'sub1C')", "_post_attach('sub1Ca', 'sub1C')", ] TNode.TRACKING.clear() # change parent s1ca.parent = s0 # break tree into two s1.parent = None # set to the same s1b.parent = s1 assert TNode.TRACKING == [ "_pre_detach('sub1Ca', 'sub1C')", "_post_detach('sub1Ca', 'sub1C')", "_pre_attach('sub1Ca', 'sub0')", "_post_attach('sub1Ca', 'sub0')", "_pre_detach('sub1', 'root')", "_post_detach('sub1', 'root')", ] TNode.TRACKING.clear() def test_detach_children(): root = TNode("root") s0 = TNode("sub0", parent=root) s0b = TNode("sub0B", parent=s0) s0a = TNode("sub0A", parent=s0) s1 = TNode("sub1", parent=root) s1a = TNode("sub1A", parent=s1) s1b = TNode("sub1B", parent=s1) s1c = TNode("sub1C", parent=s1) s1ca = TNode("sub1Ca", parent=s1c) assert TNode.TRACKING == [ "_pre_attach('sub0', 'root')", "_post_attach('sub0', 'root')", "_pre_attach('sub0B', 'sub0')", "_post_attach('sub0B', 'sub0')", "_pre_attach('sub0A', 'sub0')", "_post_attach('sub0A', 'sub0')", "_pre_attach('sub1', 'root')", "_post_attach('sub1', 'root')", "_pre_attach('sub1A', 'sub1')", "_post_attach('sub1A', 'sub1')", "_pre_attach('sub1B', 'sub1')", "_post_attach('sub1B', 'sub1')", "_pre_attach('sub1C', 'sub1')", "_post_attach('sub1C', 'sub1')", "_pre_attach('sub1Ca', 'sub1C')", "_post_attach('sub1Ca', 'sub1C')", ] TNode.TRACKING.clear() del s0.children assert TNode.TRACKING == [ "_pre_detach_children('sub0', ('sub0B', 'sub0A'))", "_pre_detach('sub0B', 'sub0')", "_post_detach('sub0B', 'sub0')", "_pre_detach('sub0A', 'sub0')", "_post_detach('sub0A', 'sub0')", "_post_detach_children('sub0', ('sub0B', 'sub0A'))", ] TNode.TRACKING.clear() del s1.children assert TNode.TRACKING == [ "_pre_detach_children('sub1', ('sub1A', 'sub1B', 'sub1C'))", "_pre_detach('sub1A', 'sub1')", "_post_detach('sub1A', 'sub1')", "_pre_detach('sub1B', 'sub1')", "_post_detach('sub1B', 'sub1')", "_pre_detach('sub1C', 'sub1')", "_post_detach('sub1C', 'sub1')", "_post_detach_children('sub1', ('sub1A', 'sub1B', 'sub1C'))", ] TNode.TRACKING.clear() def test_children_setter(): root = TNode("root") s0 = TNode("sub0") s1 = TNode("sub0A") s0a = TNode("sub0B") assert TNode.TRACKING == [] TNode.TRACKING.clear() root.children = [s0, s1] s0.children = [s0a] assert TNode.TRACKING == [ "_pre_detach_children('root', ())", "_post_detach_children('root', ())", "_pre_attach_children('root', ('sub0', 'sub0A'))", "_pre_attach('sub0', 'root')", "_post_attach('sub0', 'root')", "_pre_attach('sub0A', 'root')", "_post_attach('sub0A', 'root')", "_post_attach_children('root', ('sub0', 'sub0A'))", "_pre_detach_children('sub0', ())", "_post_detach_children('sub0', ())", "_pre_attach_children('sub0', ('sub0B',))", "_pre_attach('sub0B', 'sub0')", "_post_attach('sub0B', 'sub0')", "_post_attach_children('sub0', ('sub0B',))", ] TNode.TRACKING.clear() with assert_raises(LoopError, "Cannot set parent. TNode('/root/sub0') cannot be parent of itself."): s0.children = [s0] # test whether tree is unchanged after LoopError assert TNode.TRACKING == [ "_pre_detach_children('sub0', ('sub0B',))", "_pre_detach('sub0B', 'sub0')", "_post_detach('sub0B', 'sub0')", "_post_detach_children('sub0', ('sub0B',))", "_pre_attach_children('sub0', ('sub0',))", "_pre_detach_children('sub0', ())", "_post_detach_children('sub0', ())", "_pre_attach_children('sub0', ('sub0B',))", "_pre_attach('sub0B', 'sub0')", "_post_attach('sub0B', 'sub0')", "_post_attach_children('sub0', ('sub0B',))", ] TNode.TRACKING.clear() with assert_raises(LoopError, "Cannot set parent. TNode('/root/sub0') is parent of TNode('/root/sub0/sub0B')."): s0a.children = [s0] # test whether tree is unchanged after LoopError assert TNode.TRACKING == [ "_pre_detach_children('sub0B', ())", "_post_detach_children('sub0B', ())", "_pre_attach_children('sub0B', ('sub0',))", "_pre_detach_children('sub0B', ())", "_post_detach_children('sub0B', ())", "_pre_attach_children('sub0B', ())", "_post_attach_children('sub0B', ())", ] TNode.TRACKING.clear() root.children = [s0, s1] assert TNode.TRACKING == [ "_pre_detach_children('root', ('sub0', 'sub0A'))", "_pre_detach('sub0', 'root')", "_post_detach('sub0', 'root')", "_pre_detach('sub0A', 'root')", "_post_detach('sub0A', 'root')", "_post_detach_children('root', ('sub0', 'sub0A'))", "_pre_attach_children('root', ('sub0', 'sub0A'))", "_pre_attach('sub0', 'root')", "_post_attach('sub0', 'root')", "_pre_attach('sub0A', 'root')", "_post_attach('sub0A', 'root')", "_post_attach_children('root', ('sub0', 'sub0A'))", ] TNode.TRACKING.clear() s0.children = [s0a] assert TNode.TRACKING == [ "_pre_detach_children('sub0', ('sub0B',))", "_pre_detach('sub0B', 'sub0')", "_post_detach('sub0B', 'sub0')", "_post_detach_children('sub0', ('sub0B',))", "_pre_attach_children('sub0', ('sub0B',))", "_pre_attach('sub0B', 'sub0')", "_post_attach('sub0B', 'sub0')", "_post_attach_children('sub0', ('sub0B',))", ] TNode.TRACKING.clear() s0a.children = [s1] assert TNode.TRACKING == [ "_pre_detach_children('sub0B', ())", "_post_detach_children('sub0B', ())", "_pre_attach_children('sub0B', ('sub0A',))", "_pre_detach('sub0A', 'root')", "_post_detach('sub0A', 'root')", "_pre_attach('sub0A', 'sub0B')", "_post_attach('sub0A', 'sub0B')", "_post_attach_children('sub0B', ('sub0A',))", ] TNode.TRACKING.clear() root.children = [s0, s1] assert TNode.TRACKING == [ "_pre_detach_children('root', ('sub0',))", "_pre_detach('sub0', 'root')", "_post_detach('sub0', 'root')", "_post_detach_children('root', ('sub0',))", "_pre_attach_children('root', ('sub0', 'sub0A'))", "_pre_attach('sub0', 'root')", "_post_attach('sub0', 'root')", "_pre_detach('sub0A', 'sub0B')", "_post_detach('sub0A', 'sub0B')", "_pre_attach('sub0A', 'root')", "_post_attach('sub0A', 'root')", "_post_attach_children('root', ('sub0', 'sub0A'))", ] TNode.TRACKING.clear() anytree-2.13.0/tests/test_node_integrity.py000066400000000000000000000034131500063244000210350ustar00rootroot00000000000000from anytree import Node from .helper import assert_raises, eq_ def test_readonly_pre(): """Read Only Use case, where Exceptions in _pre_{attach,detach} avoid modifications.""" class ReadonlyError(RuntimeError): pass class ReadonlyNode(Node): _is_readonly = False def _pre_attach(self, parent): if self._is_readonly: raise ReadonlyError def _pre_detach(self, parent): if self._is_readonly: raise ReadonlyError # construct and make readonly! root = ReadonlyNode("root") s0 = ReadonlyNode("sub0", parent=root) s0b = ReadonlyNode("sub0B", parent=s0) s0a = ReadonlyNode("sub0A", parent=s0) s1 = ReadonlyNode("sub1", parent=root) s1a = ReadonlyNode("sub1A", parent=s1) s1b = ReadonlyNode("sub1B", parent=s1) s1c = ReadonlyNode("sub1C", parent=s1) s1ca = ReadonlyNode("sub1Ca", parent=s1c) ReadonlyNode._is_readonly = True def check(): eq_(root.parent, None) eq_(root.children, (s0, s1)) eq_(s0.parent, root) eq_(s0.children, (s0b, s0a)) eq_(s0b.parent, s0) eq_(s0b.children, ()) eq_(s0a.parent, s0) eq_(s0a.children, ()) eq_(s1.parent, root) eq_(s1.children, (s1a, s1b, s1c)) eq_(s1a.parent, s1) eq_(s1a.children, ()) eq_(s1b.parent, s1) eq_(s1b.children, ()) eq_(s1c.parent, s1) eq_(s1c.children, (s1ca,)) eq_(s1ca.parent, s1c) eq_(s1ca.children, ()) check() with assert_raises(ReadonlyError, ""): s1ca.parent = s0 check() with assert_raises(ReadonlyError, ""): s1ca.parent = None check() with assert_raises(ReadonlyError, ""): s0.children = [] check() anytree-2.13.0/tests/test_node_sep.py000066400000000000000000000044561500063244000176160ustar00rootroot00000000000000"""Test custom node separator.""" import anytree as at from .helper import assert_raises, eq_ class MyNode(at.Node): separator = "|" def test_render(): """Render string cast.""" root = MyNode("root") s0 = MyNode("sub0", parent=root) MyNode("sub0B", parent=s0) MyNode("sub0A", parent=s0) MyNode("sub1", parent=root) r = at.RenderTree(root) assert str(r).splitlines() == [ "MyNode('|root')", "├── MyNode('|root|sub0')", "│ ├── MyNode('|root|sub0|sub0B')", "│ └── MyNode('|root|sub0|sub0A')", "└── MyNode('|root|sub1')", ] def test_get(): """Get.""" top = MyNode("top", parent=None) sub0 = MyNode("sub0", parent=top) sub0sub0 = MyNode("sub0sub0", parent=sub0) sub0sub1 = MyNode("sub0sub1", parent=sub0) sub1 = MyNode("sub1", parent=top) r = at.Resolver("name") eq_(r.get(top, "sub0|sub0sub0"), sub0sub0) eq_(r.get(sub1, ".."), top) eq_(r.get(sub1, "..|sub0|sub0sub1"), sub0sub1) eq_(r.get(sub1, "."), sub1) eq_(r.get(sub1, ""), sub1) with assert_raises(at.ChildResolverError, "MyNode('|top') has no child sub2. Children are: 'sub0', 'sub1'."): r.get(top, "sub2") eq_(r.get(sub0sub0, "|top"), top) eq_(r.get(sub0sub0, "|top|sub0"), sub0) with assert_raises(at.ResolverError, "root node missing. root is '|top'."): r.get(sub0sub0, "|") with assert_raises(at.ResolverError, "unknown root node '|bar'. root is '|top'."): r.get(sub0sub0, "|bar") def test_glob(): """Wildcard.""" top = MyNode("top", parent=None) sub0 = MyNode("sub0", parent=top) sub0sub0 = MyNode("sub0", parent=sub0) sub0sub1 = MyNode("sub1", parent=sub0) sub0sub1sub0 = MyNode("sub0", parent=sub0sub1) MyNode("sub1", parent=sub0sub1) sub1 = MyNode("sub1", parent=top) sub1sub0 = MyNode("sub0", parent=sub1) r = at.Resolver() eq_(r.glob(top, "*|*|sub0"), [sub0sub1sub0]) eq_(r.glob(top, "sub0|sub?"), [sub0sub0, sub0sub1]) eq_(r.glob(sub1, "..|.|*"), [sub0, sub1]) eq_(r.glob(top, "*|*"), [sub0sub0, sub0sub1, sub1sub0]) eq_(r.glob(top, "*|sub0"), [sub0sub0, sub1sub0]) with assert_raises(at.ChildResolverError, "MyNode('|top|sub1') has no child sub1. Children are: 'sub0'."): r.glob(top, "sub1|sub1") anytree-2.13.0/tests/test_node_symlink.py000066400000000000000000000027651500063244000205160ustar00rootroot00000000000000from anytree import Node, PostOrderIter, PreOrderIter, SymlinkNode from .helper import eq_ def test_symlink(): root = Node("root") s0 = Node("sub0", parent=root) s0b = Node("sub0B", parent=s0) s0a = Node("sub0A", parent=s0) s1 = Node("sub1", parent=root, foo=4) s1a = Node("sub1A", parent=s1) s1b = Node("sub1B", parent=s1) s1c = Node("sub1C", parent=s1) s1ca = Node("sub1Ca", parent=s1c) ln = SymlinkNode(s1, parent=root, blub=17) l0 = Node("l0", parent=ln) eq_(root.parent, None) eq_(root.children, (s0, s1, ln)) eq_(s0.parent, root) eq_(s0.children, (s0b, s0a)) eq_(s0b.parent, s0) eq_(s0b.children, ()) eq_(s0a.parent, s0) eq_(s0a.children, ()) eq_(s1.parent, root) eq_(s1.children, (s1a, s1b, s1c)) eq_(s1.foo, 4) eq_(s1a.parent, s1) eq_(s1a.children, ()) eq_(s1b.parent, s1) eq_(s1b.children, ()) eq_(s1c.parent, s1) eq_(s1c.children, (s1ca,)) eq_(s1ca.parent, s1c) eq_(s1ca.children, ()) eq_(ln.parent, root) eq_(ln.children, (l0,)) eq_(ln.foo, 4) eq_(s1.blub, 17) eq_(ln.blub, 17) ln.bar = 9 eq_(ln.bar, 9) eq_(s1.bar, 9) result = [node.name for node in PreOrderIter(root)] eq_(result, ["root", "sub0", "sub0B", "sub0A", "sub1", "sub1A", "sub1B", "sub1C", "sub1Ca", "sub1", "l0"]) result = [node.name for node in PostOrderIter(root)] eq_(result, ["sub0B", "sub0A", "sub0", "sub1A", "sub1B", "sub1Ca", "sub1C", "sub1", "l0", "sub1", "root"]) anytree-2.13.0/tests/test_pickle.py000066400000000000000000000013511500063244000172600ustar00rootroot00000000000000import pickle from anytree import Node, RenderTree, SymlinkNode def test_pickle(tmp_path): """Pickling Compatibility.""" root = Node(name="root") a = Node(name="a", parent=root) b = Node(name="b", parent=a) c = SymlinkNode(target=b, parent=a) lines = str(RenderTree(root)).splitlines() assert lines == [ "Node('/root')", "└── Node('/root/a')", " ├── Node('/root/a/b')", " └── SymlinkNode(Node('/root/a/b'))", ] filepath = tmp_path / "test.pkl" with open(filepath, "wb") as file: pickle.dump(root, file) with open(filepath, "rb") as file: loaded = pickle.load(file) assert str(RenderTree(loaded)).splitlines() == lines anytree-2.13.0/tests/test_render.py000066400000000000000000000116651500063244000173010ustar00rootroot00000000000000import anytree def test_render_str(): """Render string cast.""" root = anytree.Node("root") s0 = anytree.Node("sub0", parent=root) anytree.Node("sub0B", parent=s0) anytree.Node("sub0A", parent=s0) anytree.Node("sub1", parent=root) r = anytree.RenderTree(root) assert str(r).splitlines() == [ "Node('/root')", "├── Node('/root/sub0')", "│ ├── Node('/root/sub0/sub0B')", "│ └── Node('/root/sub0/sub0A')", "└── Node('/root/sub1')", ] r = anytree.RenderTree(root, childiter=lambda nodes: [n for n in nodes if len(n.name) < 5]) assert str(r).splitlines() == [ "Node('/root')", "├── Node('/root/sub0')", "└── Node('/root/sub1')", ] def test_render_repr(): """Render representation.""" root = anytree.Node("root") anytree.Node("sub", parent=root) r = anytree.RenderTree(root) assert repr(r) == "RenderTree(Node('/root'), style=ContStyle(), childiter=)" def test_render(): """Rendering.""" root = anytree.Node("root", lines=["c0fe", "c0de"]) s0 = anytree.Node("sub0", parent=root, lines=["ha", "ba"]) s0b = anytree.Node("sub0B", parent=s0, lines=["1", "2", "3"]) s0a = anytree.Node("sub0A", parent=s0, lines=["a", "b"]) s1 = anytree.Node("sub1", parent=root, lines=["Z"]) r = anytree.RenderTree(root, style=anytree.DoubleStyle) result = [(pre, node) for pre, _, node in r] assert result == [ ("", root), ("â• â•�â•� ", s0), ("â•‘ â• â•�â•� ", s0b), ("â•‘ ╚â•�â•� ", s0a), ("╚â•�â•� ", s1), ] def multi(root): for pre, fill, node in anytree.RenderTree(root): yield f"{pre}{node.lines[0]}", node for line in node.lines[1:]: yield f"{fill}{line}", node assert list(multi(root)) == [ ("c0fe", root), ("c0de", root), ("├── ha", s0), ("│ ba", s0), ("│ ├── 1", s0b), ("│ │ 2", s0b), ("│ │ 3", s0b), ("│ └── a", s0a), ("│ b", s0a), ("└── Z", s1), ] def test_maxlevel(): root = anytree.Node("root", lines=["c0fe", "c0de"]) s0 = anytree.Node("sub0", parent=root, lines=["ha", "ba"]) s0b = anytree.Node("sub0B", parent=s0, lines=["1", "2", "3"]) s0a = anytree.Node("sub0A", parent=s0, lines=["a", "b"]) s1 = anytree.Node("sub1", parent=root, lines=["Z"]) r = anytree.RenderTree(root, maxlevel=2) result = [(pre, node) for pre, _, node in r] assert result == [ ("", root), ("├── ", s0), ("└── ", s1), ] def test_asciistyle(): style = anytree.AsciiStyle() assert style.vertical == "| " assert style.cont == "|-- " assert style.end == "+-- " def test_contstyle(): style = anytree.ContStyle() assert style.vertical == "\u2502 " assert style.cont == "\u251c\u2500\u2500 " assert style.end == "\u2514\u2500\u2500 " def test_controundstyle(): style = anytree.ContRoundStyle() assert style.vertical == "\u2502 " assert style.cont == "\u251c\u2500\u2500 " assert style.end == "\u2570\u2500\u2500 " def test_doublestyle(): style = anytree.DoubleStyle() assert style.vertical == "\u2551 " assert style.cont == "\u2560\u2550\u2550 " assert style.end == "\u255a\u2550\u2550 " def test_by_attr(): """By attr.""" root = anytree.Node("root", lines=["root"]) s0 = anytree.Node("sub0", parent=root, lines=["su", "b0"]) anytree.Node("sub0B", parent=s0, lines=["sub", "0B"]) anytree.Node("sub0A", parent=s0) anytree.Node("sub1", parent=root, lines=["sub1"]) assert anytree.RenderTree(root).by_attr().splitlines() == [ "root", "├── sub0", "│ ├── sub0B", "│ └── sub0A", "└── sub1", ] assert anytree.RenderTree(root).by_attr("lines").splitlines() == [ "root", "├── su", "│ b0", "│ ├── sub", "│ │ 0B", "│ └── ", "└── sub1", ] assert anytree.RenderTree(root).by_attr(lambda node: ":".join(node.name)).splitlines() == [ "r:o:o:t", "├── s:u:b:0", "│ ├── s:u:b:0:B", "│ └── s:u:b:0:A", "└── s:u:b:1", ] def test_repr(): """Repr.""" class ReprNode(anytree.Node): def __repr__(self): return f"{self.name}\n{self.name}" root = ReprNode("root", lines=["root"]) s0 = ReprNode("sub0", parent=root, lines=["su", "b0"]) ReprNode("sub0B", parent=s0, lines=["sub", "0B"]) ReprNode("sub0A", parent=s0) ReprNode("sub1", parent=root, lines=["sub1"]) bystr = str(anytree.RenderTree(root)).splitlines() byident = anytree.RenderTree(root).by_attr(lambda node: node).splitlines() assert bystr == byident anytree-2.13.0/tests/test_resolver.py000066400000000000000000000227421500063244000176610ustar00rootroot00000000000000from enum import IntEnum from pytest import raises import anytree as at from anytree import Node, Resolver from .helper import assert_raises def test_get(): """Get.""" top = at.Node("top", parent=None) sub0 = at.Node("sub0", parent=top) sub0sub0 = at.Node("sub0sub0", parent=sub0) sub0sub1 = at.Node("sub0sub1", parent=sub0) sub1 = at.Node("sub1", parent=top) r = at.Resolver("name") assert r.get(top, "sub0/sub0sub0") == sub0sub0 assert r.get(sub1, "..") == top assert r.get(sub1, "../") == top assert r.get(sub1, "../.") == top assert r.get(sub1, "../sub0/sub0sub1") == sub0sub1 assert r.get(sub1, ".") == sub1 assert r.get(sub1, "") == sub1 with assert_raises(at.ChildResolverError, "Node('/top') has no child sub2. Children are: 'sub0', 'sub1'."): r.get(top, "sub2") assert r.get(sub0sub0, "/top") == top assert r.get(sub0sub0, "/top/sub0") == sub0 with assert_raises(at.RootResolverError, "Cannot go above root node Node('/top')"): r.get(top, "..") with assert_raises(at.ResolverError, "root node missing. root is '/top'."): r.get(sub0sub0, "/") with assert_raises(at.ResolverError, "unknown root node '/bar'. root is '/top'."): r.get(sub0sub0, "/bar") def test_get_relaxed(): """Get in relaxed mode.""" top = at.Node("top", parent=None) sub0 = at.Node("sub0", parent=top) sub0sub0 = at.Node("sub0sub0", parent=sub0) sub0sub1 = at.Node("sub0sub1", parent=sub0) sub1 = at.Node("sub1", parent=top) r = at.Resolver("name", relax=True) assert r.get(top, "sub0/sub0sub0") == sub0sub0 assert r.get(sub1, "..") == top assert r.get(sub1, "../") == top assert r.get(sub1, "../.") == top assert r.get(sub1, "../sub0/sub0sub1") == sub0sub1 assert r.get(sub1, ".") == sub1 assert r.get(sub1, "") == sub1 assert r.get(top, "sub2") is None assert r.get(sub0sub0, "/top") == top assert r.get(sub0sub0, "/top/sub0") == sub0 assert r.get(top, "..") is None assert r.get(sub0sub0, "/") is None assert r.get(sub0sub0, "/bar") is None def test_glob(): """Wildcard.""" top = at.Node("top", parent=None) sub0 = at.Node("sub0", parent=top) sub0sub0 = at.Node("sub0", parent=sub0) sub0sub1 = at.Node("sub1", parent=sub0) sub0sub1sub0 = at.Node("sub0", parent=sub0sub1) at.Node("sub1", parent=sub0sub1) sub1 = at.Node("sub1", parent=top) sub1sub0 = at.Node("sub0", parent=sub1) r = at.Resolver() assert r.glob(top, "sub0/sub0") == [sub0sub0] assert r.glob(sub1, "..") == [top] assert r.glob(sub1, "../") == [top] assert r.glob(sub1, "../.") == [top] assert r.glob(sub1, "../././.") == [top] assert r.glob(sub1, ".././././sub0/..") == [top] assert r.glob(sub1, "../sub0/sub1") == [sub0sub1] assert r.glob(sub1, ".") == [sub1] assert r.glob(sub1, "./") == [sub1] assert r.glob(sub1, "") == [sub1] assert r.glob(sub1, "/top") == [top] assert r.glob(sub1, "/*") == [top] assert r.glob(top, "*/*/sub0") == [sub0sub1sub0] assert r.glob(top, "sub0/sub?") == [sub0sub0, sub0sub1] assert r.glob(sub1, ".././*") == [sub0, sub1] assert r.glob(top, "*/*") == [sub0sub0, sub0sub1, sub1sub0] assert r.glob(top, "*/sub0") == [sub0sub0, sub1sub0] with assert_raises(at.RootResolverError, "Cannot go above root node Node('/top')"): r.glob(top, "..") with assert_raises(at.RootResolverError, "Cannot go above root node Node('/top')"): r.glob(sub1, ".././..") with assert_raises(at.ChildResolverError, "Node('/top/sub1') has no child sub1. Children are: 'sub0'."): r.glob(top, "sub1/sub1") with assert_raises(at.ResolverError, "unknown root node '/z*'. root is '/top'."): r.glob(sub1, "/z*") # Recursive matching assert r.glob(top, "**/sub0") == [sub0, sub0sub0, sub0sub1sub0, sub1sub0] assert r.glob(top, "**/sub0/sub0") == [sub0sub0] assert r.glob(top, "**/**/sub0") == [sub0, sub0sub0, sub0sub1sub0, sub1sub0] assert r.glob(top, "sub0/**/sub0") == [sub0sub0, sub0sub1sub0] with assert_raises(at.ResolverError, "unknown root node '/sub0'. root is '/top'."): r.glob(top, "/sub0/**/sub0") def test_glob_relaxed(): """Wildcard relaxed.""" top = at.Node("top", parent=None) sub0 = at.Node("sub0", parent=top) sub0sub0 = at.Node("sub0", parent=sub0) sub0sub1 = at.Node("sub1", parent=sub0) sub0sub1sub0 = at.Node("sub0", parent=sub0sub1) at.Node("sub1", parent=sub0sub1) sub1 = at.Node("sub1", parent=top) sub1sub0 = at.Node("sub0", parent=sub1) r = at.Resolver(relax=True) assert r.glob(top, "sub0/sub0") == [sub0sub0] assert r.glob(sub1, "..") == [top] assert r.glob(sub1, "../") == [top] assert r.glob(sub1, "../.") == [top] assert r.glob(sub1, "../././.") == [top] assert r.glob(sub1, ".././././sub0/..") == [top] assert r.glob(sub1, "../sub0/sub1") == [sub0sub1] assert r.glob(sub1, ".") == [sub1] assert r.glob(sub1, "./") == [sub1] assert r.glob(sub1, "") == [sub1] assert r.glob(sub1, "/top") == [top] assert r.glob(sub1, "/*") == [top] assert r.glob(top, "*/*/sub0") == [sub0sub1sub0] assert r.glob(top, "sub0/sub?") == [sub0sub0, sub0sub1] assert r.glob(sub1, ".././*") == [sub0, sub1] assert r.glob(top, "*/*") == [sub0sub0, sub0sub1, sub1sub0] assert r.glob(top, "*/sub0") == [sub0sub0, sub1sub0] assert r.glob(top, "..") == [] assert r.glob(sub1, ".././..") == [] assert r.glob(top, "sub1/sub1") == [] assert r.glob(sub1, "/z*") == [] # Recursive matching assert r.glob(top, "**/sub0") == [sub0, sub0sub0, sub0sub1sub0, sub1sub0] assert r.glob(top, "**/sub0/sub0") == [sub0sub0] assert r.glob(top, "**/**/sub0") == [sub0, sub0sub0, sub0sub1sub0, sub1sub0] assert r.glob(top, "sub0/**/sub0") == [sub0sub0, sub0sub1sub0] assert r.glob(top, "/sub0/**/sub0") == [] def test_glob_cache(): """Wildcard Cache.""" root = at.Node("root") sub0 = at.Node("sub0", parent=root) sub1 = at.Node("sub1", parent=root) r = at.Resolver() # strip down cache size at.resolver._MAXCACHE = 2 at.Resolver._match_cache.clear() assert len(at.Resolver._match_cache) == 0 assert r.glob(root, "sub0") == [sub0] assert len(at.Resolver._match_cache) == 1 assert r.glob(root, "sub1") == [sub1] assert len(at.Resolver._match_cache) == 2 assert r.glob(root, "sub*") == [sub0, sub1] assert len(at.Resolver._match_cache) == 1 def test_same_name(): """Same Name.""" root = at.Node("root") sub0 = at.Node("sub", parent=root) sub1 = at.Node("sub", parent=root) r = at.Resolver() assert r.get(root, "sub") == sub0 assert r.glob(root, "sub") == [sub0, sub1] def test_ignorecase(): """Case insensitive resolver.""" root = at.Node("root") sub0 = at.Node("sUB0", parent=root) sub1 = at.Node("sub1", parent=root) r = at.Resolver(ignorecase=True) assert r.get(root, "SUB0") == sub0 assert r.get(root, "sub0") == sub0 assert r.get(root, "sUB0") == sub0 assert r.glob(root, "SU*1") == [sub1] assert r.glob(root, "/*/SU*1") == [sub1] assert r.glob(sub0, "../*1") == [sub1] def test_enum(): class Animals(IntEnum): Mammal = 1 Cat = 2 Dog = 3 root = Node("ANIMAL") mammal = Node(Animals.Mammal, parent=root) cat = Node(Animals.Cat, parent=mammal) dog = Node(Animals.Dog, parent=mammal) r = Resolver() assert r.glob(root, "/ANIMAL/*") == [mammal] assert r.glob(root, "/ANIMAL/*/*") == [cat, dog] def test_glob_consistency(): """Ensure Consistency.""" node = at.Node("root") resolver = at.Resolver() assert resolver.glob(node, "/root") == [node] assert resolver.get(node, "/root") == node assert resolver.glob(node, ".") == [node] assert resolver.get(node, ".") == node assert resolver.glob(node, "") == [node] assert resolver.get(node, "") == node with raises(at.ResolverError): assert resolver.glob(node, "/") with raises(at.ResolverError): assert resolver.get(node, "/") with raises(at.RootResolverError): assert resolver.glob(node, "..") with raises(at.RootResolverError): assert resolver.get(node, "..") with raises(at.ResolverError): assert resolver.glob(node, "root") with raises(at.ResolverError): assert resolver.get(node, "root") with raises(at.ResolverError): assert resolver.glob(node, "bla") with raises(at.ResolverError): assert resolver.get(node, "bla") with raises(at.ResolverError): assert resolver.glob(node, "/bla") with raises(at.ResolverError): assert resolver.get(node, "/bla") def test_glob_consistency_relax(): """Ensure Consistency on relaxed.""" node = at.Node("root") resolver = at.Resolver(relax=True) assert resolver.glob(node, "/root") == [node] assert resolver.get(node, "/root") == node assert resolver.glob(node, ".") == [node] assert resolver.get(node, ".") == node assert resolver.glob(node, "") == [node] assert resolver.get(node, "") == node assert resolver.glob(node, "/") == [] assert resolver.get(node, "/") is None assert resolver.glob(node, "..") == [] assert resolver.get(node, "..") is None assert resolver.glob(node, "root") == [] assert resolver.get(node, "root") is None assert resolver.glob(node, "bla") == [] assert resolver.get(node, "bla") is None assert resolver.glob(node, "/bla") == [] assert resolver.get(node, "/bla") is None anytree-2.13.0/tests/test_search.py000066400000000000000000000051241500063244000172600ustar00rootroot00000000000000from enum import IntEnum from anytree import CountError, Node, find, find_by_attr, findall, findall_by_attr from .helper import assert_raises, eq_ def test_findall(): f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) eq_(findall(f, filter_=lambda node: node.name in ("a", "b")), (b, a)) eq_(findall(f, filter_=lambda node: d in node.path), (d, c, e)) with assert_raises( CountError, ("Expecting at least 4 elements, but found 3. (Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e'))"), ): findall(f, filter_=lambda node: d in node.path, mincount=4) with assert_raises( CountError, ("Expecting 2 elements at maximum, but found 3. (Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e'))"), ): findall(f, filter_=lambda node: d in node.path, maxcount=2) def test_findall_by_attr(): f = Node("f") b = Node("b", parent=f) Node("a", parent=b) d = Node("d", parent=b) Node("c", parent=d) Node("e", parent=d) eq_(findall_by_attr(f, "d"), (d,)) with assert_raises(CountError, ("Expecting at least 1 elements, but found 0.")): findall_by_attr(f, "z", mincount=1) def test_find(): f = Node("f") b = Node("b", parent=f) Node("a", parent=b) d = Node("d", parent=b) Node("c", parent=d) Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) Node("h", parent=i) eq_(find(f, lambda n: n.name == "d"), d) eq_(find(f, lambda n: n.name == "z"), None) with assert_raises( CountError, ( "Expecting 1 elements at maximum, but found 5. " "(Node('/f/b'), Node('/f/b/a'), Node('/f/b/d'), Node('/f/b/d/c'), Node('/f/b/d/e'))" ), ): find(f, lambda n: b in n.path) def test_find_by_attr(): f = Node("f") b = Node("b", parent=f) Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d, foo=4) Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) Node("h", parent=i) eq_(find_by_attr(f, "d"), d) eq_(find_by_attr(f, name="foo", value=4), c) eq_(find_by_attr(f, name="foo", value=8), None) def test_enum(): class Animals(IntEnum): Mammal = 1 Cat = 2 Dog = 3 root = Node("ANIMAL") mammal = Node(Animals.Mammal, parent=root) cat = Node(Animals.Cat, parent=mammal) dog = Node(Animals.Dog, parent=mammal) eq_(findall(root), (root, mammal, cat, dog)) eq_(findall_by_attr(root, Animals.Cat), (cat,)) anytree-2.13.0/tests/test_special_methods_access.py000066400000000000000000000224251500063244000225020ustar00rootroot00000000000000""" The methods of the `NodeMixin` class should not access the user class special methods. For instance, the user define a `MyNode` class as below: ```python from anytree import NodeMixin class MyNode(NodeMixin): def __init__(self, name, parent=None, children=None): super(MyNode, self).__init__() self.name = name self.parent = parent if children: self.children = children ``` In this class, the used can implement some special methods, like ``__eq__`` or ``__len__``, which can have a specific meaning not related to the Tree structure. A good example could be a `NodeMixin` subclass which also implements `collections.abc.Mapping`: ```python import collections from anytree import NodeMixin class MyMapping(NodeMixin, collections.abc.Mapping): def __init__(self, name, parent=None, children=None): super(MyMapping, self).__init__() self.name = name self.parent = parent if children: self.children = children def __iter__(self): for child in self.children: yield child for item in child: yield item def __len__(self): return len(list(iter(self))) def __getitem__(self, name): for child in self: if child.name == name: return child raise KeyError(name) ``` In this example, the `NodeMixin` class shouldn't make any call to `__iter__`, `__len__` or `__getitem__`. To avoid that, the `NodeMixin` class should respect the following rules: - Do not compare nodes by value but by reference. In other words, the `NodeMixin` class should not use `==` or `!=` but compare nodes with the `id()` function. Comparison could be done with the `is` or `is not` operator. Avoid using `in` or `not in`. - Do not use truth value testing to check if a node is "empty". The `NodeMixin` class should not `if node`, `if not node`, `while node` nor `while not node`. Instead, it must compare the node with `None` like this: `if node is not None`, `if node is None`, `while node is not None` or `while node is None`. - Do not presume that nodes are hashable. The `NodeMixin` class should not store nodes in `set` or `dict`. Instead, it can store node IDs using the `id()` function. """ import functools import unittest from collections.abc import Mapping from anytree import NodeMixin # List of method names to which we want to control access: # # - This list does not contain the access control methods (`__getattribute__`, `__getattr__`, # `__setattr__`, and `__delattr__`) which are used by the NodeMixin class anyway. # - This list does not contain `__new__`, `__init__`, and `__del__` which are required for testing. # - Some methods are not available in all Python version: `__bytes__`, `__unicode__` and `__dir__`. SPECIAL_METHODS = [ # rich comparison methods "__lt__", "__le__", "__eq__", "__ne__", "__gt__", "__ge__", # repr/str/bytes "__repr__", "__str__", # "__bytes__", # "__unicode__", "__format__", # hashing "__hash__", # attribute listing # "__dir__", # pickle protocol "__getnewargs_ex__", "__getnewargs__", "__getstate__", "__setstate__", "__reduce_ex__", # object size "__sizeof__", # truth value testing "__bool__", # callable objects "__call__", # container types "__len__", "__length_hint__", "__getitem__", "__setitem__", "__delitem__", "__missing__", "__iter__", "__reversed__", "__contains__", # numeric types "__add__", "__sub__", "__mul__", "__matmul__", "__truediv__", "__floordiv__", "__mod__", "__divmod__", "__pow__", "__lshift__", "__rshift__", "__and__", "__xor__", "__or__", "__radd__", "__rsub__", "__rmul__", "__rmatmul__", "__rtruediv__", "__rfloordiv__", "__rmod__", "__rdivmod__", "__rpow__", "__rlshift__", "__rrshift__", "__rand__", "__rxor__", "__ror__", "__iadd__", "__isub__", "__imul__", "__imatmul__", "__itruediv__", "__ifloordiv__", "__imod__", "__ipow__", "__ilshift__", "__irshift__", "__iand__", "__ixor__", "__ior__", "__neg__", "__pos__", "__abs__", "__invert__", "__complex__", "__int__", "__float__", "__index__", "__round__", "__trunc__", "__floor__", "__ceil__", # With Statement Context Managers "__enter__", "__exit__", ] SPECIAL_METHODS += [attr for attr in ["__bytes__", "__unicode__", "__dir"] if hasattr(object, attr)] def prevent_access(attr, *args, **kwargs): raise AssertionError("invalid call to " + attr) class MyNode(NodeMixin): @staticmethod def __new__(cls, *args, **kwargs): for attr in SPECIAL_METHODS: setattr(cls, attr, functools.partial(prevent_access, attr)) return super(NodeMixin, cls).__new__(cls) def __init__(self, name, parent=None, children=None): super().__init__() self.name = name self.parent = parent if children: self.children = children class TestConsistency(unittest.TestCase): """Control the access to special methods.""" def setUp(self): super().setUp() self.root1 = MyNode("root1") self.child1 = MyNode("child1", parent=self.root1) self.child2a = MyNode("child2a", parent=self.child1) self.child2b = MyNode("child2b", parent=self.child1) self.other = MyNode("other") def test_parent__root1(self): _ = self.root1.parent def test_parent__setter__root1(self): self.root1.parent = self.other def test_children__root1(self): _ = self.root1.children def test_children__setter__root1(self): self.root1.children = [self.other] def test_path__root1(self): _ = self.root1.path def test_iter_path_reverse__root1(self): for _ in self.root1.iter_path_reverse(): pass def test_ancestors__root1(self): _ = self.root1.ancestors def test_descendants__root1(self): _ = self.root1.descendants def test_root__root1(self): _ = self.root1.root def test_siblings__root1(self): _ = self.root1.siblings def test_leaves__root1(self): _ = self.root1.leaves def test_is_leaf__root1(self): _ = self.root1.is_leaf def test_is_root__root1(self): _ = self.root1.is_root def test_height__root1(self): _ = self.root1.height def test_depth__root1(self): _ = self.root1.depth def test_parent__child2b(self): _ = self.child2b.parent def test_parent__setter__child2b(self): self.child2b.parent = self.other def test_children__child2b(self): _ = self.child2b.children def test_children__setter__child2b(self): self.child2b.children = [self.other] def test_path__child2b(self): _ = self.child2b.path def test_iter_path_reverse__child2b(self): for _ in self.child2b.iter_path_reverse(): pass def test_ancestors__child2b(self): _ = self.child2b.ancestors def test_descendants__child2b(self): _ = self.child2b.descendants def test_root__child2b(self): _ = self.child2b.root def test_siblings__child2b(self): _ = self.child2b.siblings def test_leaves__child2b(self): _ = self.child2b.leaves def test_is_leaf__child2b(self): _ = self.child2b.is_leaf def test_is_root__child2b(self): _ = self.child2b.is_root def test_height__child2b(self): _ = self.child2b.height def test_depth__child2b(self): _ = self.child2b.depth class MyMapping(NodeMixin, Mapping): """ This class is used to demonstrate a possible implementation which defines some special methods. """ def __init__(self, name, parent=None, children=None): super().__init__() self.name = name self.parent = parent if children: self.children = children def __iter__(self): """Iterate over all children recursively.""" for child in self.children: yield child yield from child def __len__(self): """Total number of children.""" return len(list(iter(self))) def __getitem__(self, name): for child in self: if child.name == name: return child raise KeyError(name) class TestMyMapping(unittest.TestCase): def setUp(self): super().setUp() self.root1 = MyMapping("root1") self.child1 = MyMapping("child1", parent=self.root1) self.child2a = MyMapping("child2a", parent=self.child1) self.child2b = MyMapping("child2b", parent=self.child1) def test_iter(self): expected_list = [self.child1, self.child2a, self.child2b] for actual, expected in zip(iter(self.root1), expected_list): self.assertIs(actual, expected) def test_len(self): self.assertEqual(len(self.root1), 3) def test_getitem(self): self.assertIs(self.root1["child1"], self.child1) self.assertIs(self.root1["child2a"], self.child2a) self.assertIs(self.root1["child2b"], self.child2b) with self.assertRaises(KeyError): _ = self.root1["missing"] anytree-2.13.0/tests/test_uniquedotexporter.py000066400000000000000000000042001500063244000216130ustar00rootroot00000000000000from pytest import fixture from test2ref import assert_refdata from anytree import Node from anytree.exporter import UniqueDotExporter @fixture def root(): root = Node("root") s0 = Node("sub0", parent=root, edge=2) Node("sub0B", parent=s0, foo=4, edge=109) Node("sub0A", parent=s0, edge="") s1 = Node("sub1", parent=root, edge="") Node("sub1A", parent=s1, edge=7) Node('sub1"B', parent=s1, edge=8) s1c = Node("su\\b1C", parent=s1, edge=22) Node("sub1Ca", parent=s1c, edge=42) yield root def test_tree(tmp_path, root): """Tree.""" UniqueDotExporter(root).to_dotfile(tmp_path / "tree.dot") assert_refdata(test_tree, tmp_path) def test_tree_custom(tmp_path, root): """Tree Custom.""" def nodenamefunc(node): return f"{node.name}:{node.depth}" def edgeattrfunc(node, child): return f'label="{node.name}:{child.name}"' def nodefunc(node): return f'("{node.name}")' def edgefunc(node, child): return f"--{child.edge}-->" UniqueDotExporter( root, options=["rankdir=LR;"], nodenamefunc=nodenamefunc, nodeattrfunc=lambda node: "shape=box", edgeattrfunc=edgeattrfunc, ).to_dotfile(tmp_path / "tree_custom.dot") assert_refdata(test_tree_custom, tmp_path) def test_tree_filter(tmp_path, root): """Tree with Filter.""" UniqueDotExporter(root, filter_=lambda node: node.name.startswith("sub")).to_dotfile(tmp_path / "tree_filter.dot") assert_refdata(test_tree_filter, tmp_path) def test_tree_stop(tmp_path, root): """Tree with stop.""" UniqueDotExporter(root, stop=lambda node: node.name == "sub1").to_dotfile(tmp_path / "tree_stop.dot") assert_refdata(test_tree_stop, tmp_path) def test_tree_maxlevel(tmp_path, root): """Tree with maxlevel.""" UniqueDotExporter(root, maxlevel=2).to_dotfile(tmp_path / "tree_maxlevel.dot") assert_refdata(test_tree_maxlevel, tmp_path) def test_esc(): """Test proper escape of quotes.""" n = Node(r'6"-6\"') assert tuple(UniqueDotExporter(n)) == ( "digraph tree {", ' "0x0" [label="6"-6\\""];', "}", ) anytree-2.13.0/tests/test_util.py000066400000000000000000000022411500063244000167650ustar00rootroot00000000000000from anytree import Node from anytree.util import commonancestors, leftsibling, rightsibling from .helper import eq_ def test_commonancestors(): """Commonancestors.""" udo = Node("Udo") marc = Node("Marc", parent=udo) lian = Node("Lian", parent=marc) dan = Node("Dan", parent=udo) jet = Node("Jet", parent=dan) joe = Node("Joe", parent=dan) eq_(commonancestors(jet, joe), (udo, dan)) eq_(commonancestors(jet, marc), (udo,)) eq_(commonancestors(jet), (udo, dan)) eq_(commonancestors(), ()) eq_(commonancestors(jet, lian), (udo,)) def test_leftsibling(): """Leftsibling.""" dan = Node("Dan") jet = Node("Jet", parent=dan) jan = Node("Jan", parent=dan) joe = Node("Joe", parent=dan) eq_(leftsibling(dan), None) eq_(leftsibling(jet), None) eq_(leftsibling(jan), jet) eq_(leftsibling(joe), jan) def test_rightsibling(): """Rightsibling.""" dan = Node("Dan") jet = Node("Jet", parent=dan) jan = Node("Jan", parent=dan) joe = Node("Joe", parent=dan) eq_(rightsibling(dan), None) eq_(rightsibling(jet), jan) eq_(rightsibling(jan), joe) eq_(rightsibling(joe), None) anytree-2.13.0/tests/test_walker.py000066400000000000000000000013601500063244000172760ustar00rootroot00000000000000from anytree import Node, Walker, WalkError from .helper import assert_raises, eq_ def test_walker(): """Walk test.""" f = Node("f") b = Node("b", parent=f) a = Node("a", parent=b) d = Node("d", parent=b) c = Node("c", parent=d) e = Node("e", parent=d) g = Node("g", parent=f) i = Node("i", parent=g) h = Node("h", parent=i) w = Walker() eq_(w.walk(f, f), ((), f, ())) eq_(w.walk(f, b), ((), f, (b,))) eq_(w.walk(b, f), ((b,), f, ())) eq_(w.walk(a, f), ((a, b), f, ())) eq_(w.walk(h, e), ((h, i, g), f, (b, d, e))) eq_(w.walk(d, e), ((), d, (e,))) with assert_raises(WalkError, "Node('/a') and Node('/b') are not part of the same tree."): w.walk(Node("a"), Node("b")) anytree-2.13.0/tests/testdata/000077500000000000000000000000001500063244000162115ustar00rootroot00000000000000anytree-2.13.0/tests/testdata/tree1.dot000066400000000000000000000004641500063244000177450ustar00rootroot00000000000000digraph tree { "root"; "sub0"; "sub0B"; "sub0A"; "sub1"; "sub1A"; "sub1B"; "sub1C"; "99"; "root" -> "sub0"; "root" -> "sub1"; "sub0" -> "sub0B"; "sub0" -> "sub0A"; "sub1" -> "sub1A"; "sub1" -> "sub1B"; "sub1" -> "sub1C"; "sub1C" -> "99"; } anytree-2.13.0/tests/testdata/tree2.dot000066400000000000000000000012221500063244000177370ustar00rootroot00000000000000digraph tree { rankdir=LR; "root:0" [shape=box]; "sub0:1" [shape=box]; "sub0B:2" [shape=box]; "sub0A:2" [shape=box]; "sub1:1" [shape=box]; "sub1A:2" [shape=box]; "sub1B:2" [shape=box]; "sub1C:2" [shape=box]; "sub1Ca:3" [shape=box]; "root:0" -> "sub0:1" [label="root:sub0"]; "root:0" -> "sub1:1" [label="root:sub1"]; "sub0:1" -> "sub0B:2" [label="sub0:sub0B"]; "sub0:1" -> "sub0A:2" [label="sub0:sub0A"]; "sub1:1" -> "sub1A:2" [label="sub1:sub1A"]; "sub1:1" -> "sub1B:2" [label="sub1:sub1B"]; "sub1:1" -> "sub1C:2" [label="sub1:sub1C"]; "sub1C:2" -> "sub1Ca:3" [label="sub1C:sub1Ca"]; } anytree-2.13.0/uv.lock000066400000000000000000003767241500063244000145650ustar00rootroot00000000000000version = 1 revision = 1 requires-python = ">=3.9.2, <4.0" resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] [[package]] name = "alabaster" version = "0.7.16" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776 } wheels = [ { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511 }, ] [[package]] name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, ] [[package]] name = "anytree" source = { editable = "." } [package.dev-dependencies] dev = [ { name = "coveralls" }, { name = "mypy" }, { name = "pre-commit" }, { name = "pytest-cov" }, { name = "ruff" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-rtd-theme" }, { name = "sphinxemoji" }, { name = "test2ref", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "test2ref", version = "0.8.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] [package.metadata] [package.metadata.requires-dev] dev = [ { name = "coveralls", specifier = ">=3.3.1" }, { name = "mypy", specifier = ">=1.9.0" }, { name = "pre-commit", specifier = ">=4.2.0" }, { name = "pytest-cov", specifier = ">=5.0.0" }, { name = "ruff", specifier = ">=0.11.2" }, { name = "sphinx", specifier = ">=5.3.0" }, { name = "sphinx-rtd-theme", specifier = ">=2.0.0" }, { name = "sphinxemoji", specifier = ">=0.3.1" }, { name = "test2ref", specifier = ">=0.4.2" }, ] [[package]] name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] [[package]] name = "binaryornot" version = "0.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/fe/7ebfec74d49f97fc55cd38240c7a7d08134002b1e14be8c3897c0dd5e49b/binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061", size = 371054 } wheels = [ { url = "https://files.pythonhosted.org/packages/24/7e/f7b6f453e6481d1e233540262ccbfcf89adcd43606f44a028d7f5fae5eb2/binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4", size = 9006 }, ] [[package]] name = "certifi" version = "2025.1.31" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } wheels = [ { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, ] [[package]] name = "cfgv" version = "3.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } wheels = [ { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, ] [[package]] name = "chardet" version = "5.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 } wheels = [ { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 }, ] [[package]] name = "charset-normalizer" version = "3.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } wheels = [ { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 }, { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 }, { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 }, { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 }, { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 }, { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 }, { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 }, { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 }, { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 }, { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 }, { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 }, { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 }, { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 }, { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] name = "coverage" version = "7.8.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872 } wheels = [ { url = "https://files.pythonhosted.org/packages/78/01/1c5e6ee4ebaaa5e079db933a9a45f61172048c7efa06648445821a201084/coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe", size = 211379 }, { url = "https://files.pythonhosted.org/packages/e9/16/a463389f5ff916963471f7c13585e5f38c6814607306b3cb4d6b4cf13384/coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28", size = 211814 }, { url = "https://files.pythonhosted.org/packages/b8/b1/77062b0393f54d79064dfb72d2da402657d7c569cfbc724d56ac0f9c67ed/coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3", size = 240937 }, { url = "https://files.pythonhosted.org/packages/d7/54/c7b00a23150083c124e908c352db03bcd33375494a4beb0c6d79b35448b9/coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676", size = 238849 }, { url = "https://files.pythonhosted.org/packages/f7/ec/a6b7cfebd34e7b49f844788fda94713035372b5200c23088e3bbafb30970/coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d", size = 239986 }, { url = "https://files.pythonhosted.org/packages/21/8c/c965ecef8af54e6d9b11bfbba85d4f6a319399f5f724798498387f3209eb/coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a", size = 239896 }, { url = "https://files.pythonhosted.org/packages/40/83/070550273fb4c480efa8381735969cb403fa8fd1626d74865bfaf9e4d903/coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c", size = 238613 }, { url = "https://files.pythonhosted.org/packages/07/76/fbb2540495b01d996d38e9f8897b861afed356be01160ab4e25471f4fed1/coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f", size = 238909 }, { url = "https://files.pythonhosted.org/packages/a3/7e/76d604db640b7d4a86e5dd730b73e96e12a8185f22b5d0799025121f4dcb/coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f", size = 213948 }, { url = "https://files.pythonhosted.org/packages/5c/a7/f8ce4aafb4a12ab475b56c76a71a40f427740cf496c14e943ade72e25023/coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23", size = 214844 }, { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493 }, { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921 }, { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556 }, { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245 }, { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032 }, { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679 }, { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852 }, { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389 }, { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997 }, { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911 }, { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684 }, { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935 }, { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994 }, { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885 }, { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142 }, { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906 }, { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124 }, { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317 }, { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170 }, { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969 }, { url = "https://files.pythonhosted.org/packages/f3/21/87e9b97b568e223f3438d93072479c2f36cc9b3f6b9f7094b9d50232acc0/coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", size = 211708 }, { url = "https://files.pythonhosted.org/packages/75/be/882d08b28a0d19c9c4c2e8a1c6ebe1f79c9c839eb46d4fca3bd3b34562b9/coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", size = 211981 }, { url = "https://files.pythonhosted.org/packages/7a/1d/ce99612ebd58082fbe3f8c66f6d8d5694976c76a0d474503fa70633ec77f/coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", size = 245495 }, { url = "https://files.pythonhosted.org/packages/dc/8d/6115abe97df98db6b2bd76aae395fcc941d039a7acd25f741312ced9a78f/coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", size = 242538 }, { url = "https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", size = 244561 }, { url = "https://files.pythonhosted.org/packages/22/70/c10c77cd77970ac965734fe3419f2c98665f6e982744a9bfb0e749d298f4/coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", size = 244633 }, { url = "https://files.pythonhosted.org/packages/38/5a/4f7569d946a07c952688debee18c2bb9ab24f88027e3d71fd25dbc2f9dca/coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", size = 242712 }, { url = "https://files.pythonhosted.org/packages/bb/a1/03a43b33f50475a632a91ea8c127f7e35e53786dbe6781c25f19fd5a65f8/coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", size = 244000 }, { url = "https://files.pythonhosted.org/packages/6a/89/ab6c43b1788a3128e4d1b7b54214548dcad75a621f9d277b14d16a80d8a1/coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", size = 214195 }, { url = "https://files.pythonhosted.org/packages/12/12/6bf5f9a8b063d116bac536a7fb594fc35cb04981654cccb4bbfea5dcdfa0/coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", size = 214998 }, { url = "https://files.pythonhosted.org/packages/2a/e6/1e9df74ef7a1c983a9c7443dac8aac37a46f1939ae3499424622e72a6f78/coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", size = 212541 }, { url = "https://files.pythonhosted.org/packages/04/51/c32174edb7ee49744e2e81c4b1414ac9df3dacfcb5b5f273b7f285ad43f6/coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", size = 212767 }, { url = "https://files.pythonhosted.org/packages/e9/8f/f454cbdb5212f13f29d4a7983db69169f1937e869a5142bce983ded52162/coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", size = 256997 }, { url = "https://files.pythonhosted.org/packages/e6/74/2bf9e78b321216d6ee90a81e5c22f912fc428442c830c4077b4a071db66f/coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", size = 252708 }, { url = "https://files.pythonhosted.org/packages/92/4d/50d7eb1e9a6062bee6e2f92e78b0998848a972e9afad349b6cdde6fa9e32/coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", size = 255046 }, { url = "https://files.pythonhosted.org/packages/40/9e/71fb4e7402a07c4198ab44fc564d09d7d0ffca46a9fb7b0a7b929e7641bd/coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", size = 256139 }, { url = "https://files.pythonhosted.org/packages/49/1a/78d37f7a42b5beff027e807c2843185961fdae7fe23aad5a4837c93f9d25/coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", size = 254307 }, { url = "https://files.pythonhosted.org/packages/58/e9/8fb8e0ff6bef5e170ee19d59ca694f9001b2ec085dc99b4f65c128bb3f9a/coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", size = 255116 }, { url = "https://files.pythonhosted.org/packages/56/b0/d968ecdbe6fe0a863de7169bbe9e8a476868959f3af24981f6a10d2b6924/coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", size = 214909 }, { url = "https://files.pythonhosted.org/packages/87/e9/d6b7ef9fecf42dfb418d93544af47c940aa83056c49e6021a564aafbc91f/coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", size = 216068 }, { url = "https://files.pythonhosted.org/packages/60/0c/5da94be095239814bf2730a28cffbc48d6df4304e044f80d39e1ae581997/coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f", size = 211377 }, { url = "https://files.pythonhosted.org/packages/d5/cb/b9e93ebf193a0bb89dbcd4f73d7b0e6ecb7c1b6c016671950e25f041835e/coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a", size = 211803 }, { url = "https://files.pythonhosted.org/packages/78/1a/cdbfe9e1bb14d3afcaf6bb6e1b9ba76c72666e329cd06865bbd241efd652/coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82", size = 240561 }, { url = "https://files.pythonhosted.org/packages/59/04/57f1223f26ac018d7ce791bfa65b0c29282de3e041c1cd3ed430cfeac5a5/coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814", size = 238488 }, { url = "https://files.pythonhosted.org/packages/b7/b1/0f25516ae2a35e265868670384feebe64e7857d9cffeeb3887b0197e2ba2/coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c", size = 239589 }, { url = "https://files.pythonhosted.org/packages/e0/a4/99d88baac0d1d5a46ceef2dd687aac08fffa8795e4c3e71b6f6c78e14482/coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd", size = 239366 }, { url = "https://files.pythonhosted.org/packages/ea/9e/1db89e135feb827a868ed15f8fc857160757f9cab140ffee21342c783ceb/coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4", size = 237591 }, { url = "https://files.pythonhosted.org/packages/1b/6d/ac4d6fdfd0e201bc82d1b08adfacb1e34b40d21a22cdd62cfaf3c1828566/coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899", size = 238572 }, { url = "https://files.pythonhosted.org/packages/25/5e/917cbe617c230f7f1745b6a13e780a3a1cd1cf328dbcd0fd8d7ec52858cd/coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f", size = 213966 }, { url = "https://files.pythonhosted.org/packages/bd/93/72b434fe550135869f9ea88dd36068af19afce666db576e059e75177e813/coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3", size = 214852 }, { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443 }, { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435 }, ] [package.optional-dependencies] toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] [[package]] name = "coveralls" version = "4.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "docopt" }, { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/75/a454fb443eb6a053833f61603a432ffbd7dd6ae53a11159bacfadb9d6219/coveralls-4.0.1.tar.gz", hash = "sha256:7b2a0a2bcef94f295e3cf28dcc55ca40b71c77d1c2446b538e85f0f7bc21aa69", size = 12419 } wheels = [ { url = "https://files.pythonhosted.org/packages/63/e5/6708c75e2a4cfca929302d4d9b53b862c6dc65bd75e6933ea3d20016d41d/coveralls-4.0.1-py3-none-any.whl", hash = "sha256:7a6b1fa9848332c7b2221afb20f3df90272ac0167060f41b5fe90429b30b1809", size = 13599 }, ] [[package]] name = "distlib" version = "0.3.9" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 } wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, ] [[package]] name = "docopt" version = "0.6.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901 } [[package]] name = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] [[package]] name = "exceptiongroup" version = "1.2.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } wheels = [ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, ] [[package]] name = "filelock" version = "3.18.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } wheels = [ { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, ] [[package]] name = "identify" version = "2.6.9" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/9b/98/a71ab060daec766acc30fb47dfca219d03de34a70d616a79a38c6066c5bf/identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf", size = 99249 } wheels = [ { url = "https://files.pythonhosted.org/packages/07/ce/0845144ed1f0e25db5e7a79c2354c1da4b5ce392b8966449d5db8dca18f1/identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150", size = 99101 }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] [[package]] name = "imagesize" version = "1.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } wheels = [ { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, ] [[package]] name = "importlib-metadata" version = "8.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } wheels = [ { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, ] [[package]] name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] name = "markupsafe" version = "3.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } wheels = [ { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 }, { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 }, { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 }, { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 }, { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 }, { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 }, { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 }, { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 }, { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 }, { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 }, ] [[package]] name = "mypy" version = "1.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } wheels = [ { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 }, { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 }, { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 }, { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 }, { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 }, { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 }, { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 }, { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 }, { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 }, { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, { url = "https://files.pythonhosted.org/packages/5a/fa/79cf41a55b682794abe71372151dbbf856e3008f6767057229e6649d294a/mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078", size = 10737129 }, { url = "https://files.pythonhosted.org/packages/d3/33/dd8feb2597d648de29e3da0a8bf4e1afbda472964d2a4a0052203a6f3594/mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba", size = 9856335 }, { url = "https://files.pythonhosted.org/packages/e4/b5/74508959c1b06b96674b364ffeb7ae5802646b32929b7701fc6b18447592/mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5", size = 11611935 }, { url = "https://files.pythonhosted.org/packages/6c/53/da61b9d9973efcd6507183fdad96606996191657fe79701b2c818714d573/mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b", size = 12365827 }, { url = "https://files.pythonhosted.org/packages/c1/72/965bd9ee89540c79a25778cc080c7e6ef40aa1eeac4d52cec7eae6eb5228/mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2", size = 12541924 }, { url = "https://files.pythonhosted.org/packages/46/d0/f41645c2eb263e6c77ada7d76f894c580c9ddb20d77f0c24d34273a4dab2/mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980", size = 9271176 }, { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, ] [[package]] name = "mypy-extensions" version = "1.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, ] [[package]] name = "nodeenv" version = "1.9.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] [[package]] name = "packaging" version = "24.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] [[package]] name = "platformdirs" version = "4.3.7" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } wheels = [ { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] [[package]] name = "pre-commit" version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, { name = "identify" }, { name = "nodeenv" }, { name = "pyyaml" }, { name = "virtualenv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424 } wheels = [ { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707 }, ] [[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, ] [[package]] name = "pytest" version = "8.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } wheels = [ { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, ] [[package]] name = "pytest-cov" version = "6.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857 } wheels = [ { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841 }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, ] [[package]] name = "requests" version = "2.32.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, ] [[package]] name = "roman-numerals-py" version = "3.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017 } wheels = [ { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742 }, ] [[package]] name = "ruff" version = "0.11.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e8/5b/3ae20f89777115944e89c2d8c2e795dcc5b9e04052f76d5347e35e0da66e/ruff-0.11.4.tar.gz", hash = "sha256:f45bd2fb1a56a5a85fae3b95add03fb185a0b30cf47f5edc92aa0355ca1d7407", size = 3933063 } wheels = [ { url = "https://files.pythonhosted.org/packages/9c/db/baee59ac88f57527fcbaad3a7b309994e42329c6bc4d4d2b681a3d7b5426/ruff-0.11.4-py3-none-linux_armv6l.whl", hash = "sha256:d9f4a761ecbde448a2d3e12fb398647c7f0bf526dbc354a643ec505965824ed2", size = 10106493 }, { url = "https://files.pythonhosted.org/packages/c1/d6/9a0962cbb347f4ff98b33d699bf1193ff04ca93bed4b4222fd881b502154/ruff-0.11.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8c1747d903447d45ca3d40c794d1a56458c51e5cc1bc77b7b64bd2cf0b1626cc", size = 10876382 }, { url = "https://files.pythonhosted.org/packages/3a/8f/62bab0c7d7e1ae3707b69b157701b41c1ccab8f83e8501734d12ea8a839f/ruff-0.11.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:51a6494209cacca79e121e9b244dc30d3414dac8cc5afb93f852173a2ecfc906", size = 10237050 }, { url = "https://files.pythonhosted.org/packages/09/96/e296965ae9705af19c265d4d441958ed65c0c58fc4ec340c27cc9d2a1f5b/ruff-0.11.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f171605f65f4fc49c87f41b456e882cd0c89e4ac9d58e149a2b07930e1d466f", size = 10424984 }, { url = "https://files.pythonhosted.org/packages/e5/56/644595eb57d855afed6e54b852e2df8cd5ca94c78043b2f29bdfb29882d5/ruff-0.11.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebf99ea9af918878e6ce42098981fc8c1db3850fef2f1ada69fb1dcdb0f8e79e", size = 9957438 }, { url = "https://files.pythonhosted.org/packages/86/83/9d3f3bed0118aef3e871ded9e5687fb8c5776bde233427fd9ce0a45db2d4/ruff-0.11.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edad2eac42279df12e176564a23fc6f4aaeeb09abba840627780b1bb11a9d223", size = 11547282 }, { url = "https://files.pythonhosted.org/packages/40/e6/0c6e4f5ae72fac5ccb44d72c0111f294a5c2c8cc5024afcb38e6bda5f4b3/ruff-0.11.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f103a848be9ff379fc19b5d656c1f911d0a0b4e3e0424f9532ececf319a4296e", size = 12182020 }, { url = "https://files.pythonhosted.org/packages/b5/92/4aed0e460aeb1df5ea0c2fbe8d04f9725cccdb25d8da09a0d3f5b8764bf8/ruff-0.11.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:193e6fac6eb60cc97b9f728e953c21cc38a20077ed64f912e9d62b97487f3f2d", size = 11679154 }, { url = "https://files.pythonhosted.org/packages/1b/d3/7316aa2609f2c592038e2543483eafbc62a0e1a6a6965178e284808c095c/ruff-0.11.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7af4e5f69b7c138be8dcffa5b4a061bf6ba6a3301f632a6bce25d45daff9bc99", size = 13905985 }, { url = "https://files.pythonhosted.org/packages/63/80/734d3d17546e47ff99871f44ea7540ad2bbd7a480ed197fe8a1c8a261075/ruff-0.11.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:126b1bf13154aa18ae2d6c3c5efe144ec14b97c60844cfa6eb960c2a05188222", size = 11348343 }, { url = "https://files.pythonhosted.org/packages/04/7b/70fc7f09a0161dce9613a4671d198f609e653d6f4ff9eee14d64c4c240fb/ruff-0.11.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8806daaf9dfa881a0ed603f8a0e364e4f11b6ed461b56cae2b1c0cab0645304", size = 10308487 }, { url = "https://files.pythonhosted.org/packages/1a/22/1cdd62dabd678d75842bf4944fd889cf794dc9e58c18cc547f9eb28f95ed/ruff-0.11.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5d94bb1cc2fc94a769b0eb975344f1b1f3d294da1da9ddbb5a77665feb3a3019", size = 9929091 }, { url = "https://files.pythonhosted.org/packages/9f/20/40e0563506332313148e783bbc1e4276d657962cc370657b2fff20e6e058/ruff-0.11.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:995071203d0fe2183fc7a268766fd7603afb9996785f086b0d76edee8755c896", size = 10924659 }, { url = "https://files.pythonhosted.org/packages/b5/41/eef9b7aac8819d9e942f617f9db296f13d2c4576806d604aba8db5a753f1/ruff-0.11.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a37ca937e307ea18156e775a6ac6e02f34b99e8c23fe63c1996185a4efe0751", size = 11428160 }, { url = "https://files.pythonhosted.org/packages/ff/61/c488943414fb2b8754c02f3879de003e26efdd20f38167ded3fb3fc1cda3/ruff-0.11.4-py3-none-win32.whl", hash = "sha256:0e9365a7dff9b93af933dab8aebce53b72d8f815e131796268709890b4a83270", size = 10311496 }, { url = "https://files.pythonhosted.org/packages/b6/2b/2a1c8deb5f5dfa3871eb7daa41492c4d2b2824a74d2b38e788617612a66d/ruff-0.11.4-py3-none-win_amd64.whl", hash = "sha256:5a9fa1c69c7815e39fcfb3646bbfd7f528fa8e2d4bebdcf4c2bd0fa037a255fb", size = 11399146 }, { url = "https://files.pythonhosted.org/packages/4f/03/3aec4846226d54a37822e4c7ea39489e4abd6f88388fba74e3d4abe77300/ruff-0.11.4-py3-none-win_arm64.whl", hash = "sha256:d435db6b9b93d02934cf61ef332e66af82da6d8c69aefdea5994c89997c7a0fc", size = 10450306 }, ] [[package]] name = "snowballstemmer" version = "2.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } wheels = [ { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, ] [[package]] name = "sphinx" version = "7.4.7" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "babel", marker = "python_full_version < '3.10'" }, { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, { name = "docutils", marker = "python_full_version < '3.10'" }, { name = "imagesize", marker = "python_full_version < '3.10'" }, { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "jinja2", marker = "python_full_version < '3.10'" }, { name = "packaging", marker = "python_full_version < '3.10'" }, { name = "pygments", marker = "python_full_version < '3.10'" }, { name = "requests", marker = "python_full_version < '3.10'" }, { name = "snowballstemmer", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" }, { name = "tomli", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911 } wheels = [ { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624 }, ] [[package]] name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "babel", marker = "python_full_version == '3.10.*'" }, { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, { name = "docutils", marker = "python_full_version == '3.10.*'" }, { name = "imagesize", marker = "python_full_version == '3.10.*'" }, { name = "jinja2", marker = "python_full_version == '3.10.*'" }, { name = "packaging", marker = "python_full_version == '3.10.*'" }, { name = "pygments", marker = "python_full_version == '3.10.*'" }, { name = "requests", marker = "python_full_version == '3.10.*'" }, { name = "snowballstemmer", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*'" }, { name = "tomli", marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } wheels = [ { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, ] [[package]] name = "sphinx" version = "8.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", ] dependencies = [ { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "babel", marker = "python_full_version >= '3.11'" }, { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, { name = "docutils", marker = "python_full_version >= '3.11'" }, { name = "imagesize", marker = "python_full_version >= '3.11'" }, { name = "jinja2", marker = "python_full_version >= '3.11'" }, { name = "packaging", marker = "python_full_version >= '3.11'" }, { name = "pygments", marker = "python_full_version >= '3.11'" }, { name = "requests", marker = "python_full_version >= '3.11'" }, { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876 } wheels = [ { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741 }, ] [[package]] name = "sphinx-rtd-theme" version = "3.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinxcontrib-jquery" }, ] sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463 } wheels = [ { url = "https://files.pythonhosted.org/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561 }, ] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } wheels = [ { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, ] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } wheels = [ { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, ] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } wheels = [ { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, ] [[package]] name = "sphinxcontrib-jquery" version = "4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 } wheels = [ { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 }, ] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } wheels = [ { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, ] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } wheels = [ { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, ] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, ] [[package]] name = "sphinxemoji" version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/47/36/12a061ef2a2c2001c1b083d456db680f59074aef39f9cdd83a7937a4d626/sphinxemoji-0.3.1.tar.gz", hash = "sha256:23ecff1f1e765393e49083b45386b7da81ced97c9a18a1dfd191460a97da3b11", size = 46377 } wheels = [ { url = "https://files.pythonhosted.org/packages/95/9f/c872bbf4a2588b700a972d48c6e39087e4d33b3e76c563b4902e5abfbf13/sphinxemoji-0.3.1-py3-none-any.whl", hash = "sha256:dae483695f8d1e90a28a6e9bbccc08d256202afcc1d0fbd33b51b3b4352d439e", size = 46074 }, ] [[package]] name = "test2ref" version = "0.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "binaryornot", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f9/36/fb704a853fa0419befe7d33d2fcf04874732bcf6098a62431c3ea1f079ee/test2ref-0.4.2.tar.gz", hash = "sha256:e4869d870268431ba88135e4987ba47fd24120623681f6cfd4445785b4b822e5", size = 5238 } wheels = [ { url = "https://files.pythonhosted.org/packages/08/9e/5ec8efc3d9b63dcac81098edf4c4b6cb3cdd5ea029ea69c69162abb23033/test2ref-0.4.2-py3-none-any.whl", hash = "sha256:2078db738802abdcba29f53a84f6e27b2e81a4a2490d9cf45b71dfe98991ad35", size = 5552 }, ] [[package]] name = "test2ref" version = "0.8.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] dependencies = [ { name = "binaryornot", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/52/e8/98029b29ecef425a7373a769fb6df09bc5199f2f644ef2539fd87bd2e773/test2ref-0.8.1.tar.gz", hash = "sha256:5b2c59903e34ec1837488156a8788232f7c51de78e6e2a934c1c7c0aff2033d6", size = 8801 } wheels = [ { url = "https://files.pythonhosted.org/packages/9c/c3/ed4fc5274a08d0ae0a7a68be91f849b5ac0adcf836f1c6c0223fb1159236/test2ref-0.8.1-py3-none-any.whl", hash = "sha256:e48a80eac52c6224cc39459e5d85cdfe0a30d1cbbfd0001ee20384399dbd8ca4", size = 6307 }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } wheels = [ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] [[package]] name = "typing-extensions" version = "4.13.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 } wheels = [ { url = "https://files.pythonhosted.org/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 }, ] [[package]] name = "urllib3" version = "2.3.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } wheels = [ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, ] [[package]] name = "virtualenv" version = "20.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/e0/633e369b91bbc664df47dcb5454b6c7cf441e8f5b9d0c250ce9f0546401e/virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8", size = 4346945 } wheels = [ { url = "https://files.pythonhosted.org/packages/4c/ed/3cfeb48175f0671ec430ede81f628f9fb2b1084c9064ca67ebe8c0ed6a05/virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6", size = 4329461 }, ] [[package]] name = "zipp" version = "3.21.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, ]