././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4885585 asttokens-3.0.0/0000755000076700000240000000000014722510216012551 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1715926931.0 asttokens-3.0.0/.coveragerc0000644000076700000240000000005214621573623014700 0ustar00develstaff[run] branch = True include = asttokens/* ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710615991.0 asttokens-3.0.0/.editorconfig0000644000076700000240000000014614575366667015260 0ustar00develstaff[*] indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4620495 asttokens-3.0.0/.github/0000755000076700000240000000000014722510216014111 5ustar00develstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1732939918.467233 asttokens-3.0.0/.github/workflows/0000755000076700000240000000000014722510216016146 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/.github/workflows/build-and-test.yml0000644000076700000240000000477714722467250021535 0ustar00develstaffname: Build and test on: push: branches: - master pull_request: workflow_dispatch: jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - 3.8 - 3.9 - '3.10' - 3.11 - 3.12 - 3.13 # As per https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md#pypy list of versions - pypy-3.8 - pypy-3.9 - pypy-3.10 astroid-version: - '' include: # Test recent Python on Astroid v2 too (the above natural tests will # pick up Astroid v3 for these Python versions) - python-version: '3.8' astroid-version: '<3' - python-version: '3.9' astroid-version: '<3' - python-version: '3.10' astroid-version: '<3' - python-version: '3.11' astroid-version: '<3' - python-version: '3.12' astroid-version: '<3' - python-version: '3.13' astroid-version: '<3' - python-version: 'pypy-3.10' astroid-version: '<3' env: COVERALLS_PARALLEL: true steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install dependencies run: | pip install --upgrade coveralls pytest setuptools setuptools_scm pep517 pip install .[test] 'astroid${{ matrix.astroid-version }}' - name: Mypy testing run: | # Not an exact mypy version, as we need 0.942 for pypy-3.8 support, but it's not available on 3.5 pip install "mypy>=0.910,<=0.942" python -m mypy asttokens tests/*.py - name: Fast tests with coverage run: | pytest --cov -n auto -m "not slow" coverage report -m - name: Slow tests without coverage run: | pytest -n auto -m slow - name: Collect coverage results env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pip3 install --upgrade coveralls python3 -m coveralls --service=github finish: needs: test runs-on: ubuntu-latest steps: - name: Coveralls Finished uses: AndreMiras/coveralls-python-action@v20201129 with: parallel-finished: true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1659412507.0 asttokens-3.0.0/.gitignore0000644000076700000240000000017414272120033014535 0ustar00develstaff/asttokens/version.py .coverage *.py[co] # Building and packaging build/ dist/ *.egg-info/ docs/_build/ .eggs .tox .vscode././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1540993730.0 asttokens-3.0.0/.pylintrc0000644000076700000240000003035713366331302014426 0ustar00develstaff[MASTER] # Specify a configuration file. #rcfile= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Profiled execution. profile=no # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Pickle collected data for later comparisons. persistent=yes # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Deprecated. It was used to include message's id in output. Use --msg-template # instead. include-ids=no # Deprecated. It was used to include symbolic ids of messages in output. Use # --msg-template instead. symbols=no # Use multiple processes to speed up Pylint. jobs=1 # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code extension-pkg-whitelist= # Allow optimization of some AST trees. This will activate a peephole AST # optimizer, which will apply various small optimizations. For instance, it can # be used to obtain the result of joining multiple strings with the addition # operator. Joining a lot of strings can lead to a maximum recursion error in # Pylint and this flag can prevent that. It has one side effect, the resulting # AST will be different than the one from reality. optimize-ast=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED confidence= # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time. See also the "--disable" option for examples. #enable= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once).You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" #disable=E1608,W1627,E1601,E1603,E1602,E1605,E1604,E1607,E1606,W1621,W1620,W1623,W1622,W1625,W1624,W1609,W1608,W1607,W1606,W1605,W1604,W1603,W1602,W1601,R0914,W1639,W0403,R0913,R0912,W0102,W1640,I0021,C0326,C0103,W1638,C0330,I0020,C0111,W1618,W1619,W1630,W1626,W1637,W1634,W1635,W1610,W1611,W1612,W1613,W1614,W1615,W1616,W1617,W1632,R0902,R0903,W1633,W0704,W0703,W1628,W1629,W0201,W1636,W0212 disable=invalid-name,missing-docstring [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html. You can also give a reporter class, eg # mypackage.mymodule.MyReporterClass. output-format=text # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". files-output=no # Tells whether to display a full report or only the messages reports=yes # Python expression which should return a note less than 10 (10 is the highest # note). You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Add a comment according to your evaluation note. This is used by the global # evaluation report (RP0004). comment=no # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details #msg-template= [BASIC] # Required attributes for module, separated by a comma required-attributes= # List of builtins function names that should not be used, separated by a comma bad-functions= # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Include a hint for the correct naming format with invalid-name include-naming-hint=no # Regular expression matching correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for function names function-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct variable names variable-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for variable names variable-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct constant names const-rgx=(([A-Z_][a-zA-Z0-9_]*)|(__.*__))$ # Naming hint for constant names const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Regular expression matching correct attribute names attr-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for attribute names attr-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct argument names argument-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for argument names argument-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ # Naming hint for class attribute names class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Naming hint for inline iteration names inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ # Regular expression matching correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ # Naming hint for class names class-name-hint=[A-Z_][a-zA-Z0-9]+$ # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Naming hint for module names module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression matching correct method names method-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for method names method-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=__.*__ # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 [FORMAT] # Maximum number of characters on a single line. max-line-length=100 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no # List of optional constructs for which whitespace checking is disabled no-space-check=trailing-comma,dict-separator # Maximum number of lines in a module max-module-lines=1000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=2 # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= [LOGGING] # Logging modules to check that the string format arguments are in logging # function parameter format logging-modules=logging [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME,XXX [SIMILARITIES] # Minimum lines number of a similarity. min-similarity-lines=3 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no [SPELLING] # Spelling dictionary name. Available dictionaries: none. To make it working # install python-enchant package. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no [TYPECHECK] # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=no # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis ignored-modules= # List of classes names for which member attributes should not be checked # (useful for classes with attributes dynamically set). ignored-classes=SQLObject # When zope mode is activated, add a predefined set of Zope acquired attributes # to generated-members. zope=no # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E0201 when accessed. Python regular # expressions are accepted. generated-members=REQUEST,acl_users,aq_parent,_fields,_make,simplify [VARIABLES] # Tells whether we should check for unused import in __init__ files. init-import=no # A regular expression matching the name of dummy variables (i.e. expectedly # not used). dummy-variables-rgx=_$|dummy|_*|ignore # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_,_cb [CLASSES] # List of interface methods to ignore, separated by a comma. This is used for # instance to not check methods defines in Zope's Interface base class. ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,__new__,setUp # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=mcs # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict,_fields,_replace,_source,_make [DESIGN] # Maximum number of arguments for function / method max-args=5 # Argument names that match this expression will be ignored. Default to name # with leading underscore ignored-argument-names=_.* # Maximum number of locals for function / method body max-locals=15 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branches=12 # Maximum number of statements in function / method body max-statements=50 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=2 # Maximum number of public methods for a class (see R0904). max-public-methods=100 [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled) int-import-graph= [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "Exception" overgeneral-exceptions=Exception ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693452870.0 asttokens-3.0.0/.readthedocs.yaml0000644000076700000240000000110314474005106015774 0ustar00develstaff# 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" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt - method: pip path: . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1480975766.0 asttokens-3.0.0/LICENSE0000644000076700000240000002613513021362626013566 0ustar00develstaff 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481352353.0 asttokens-3.0.0/MANIFEST.in0000644000076700000240000000007013022722241014277 0ustar00develstaffinclude README.rst LICENSE recursive-include tests *.py ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939370.0 asttokens-3.0.0/Makefile0000644000076700000240000000131414722507152014215 0ustar00develstaffdist: @echo Build python distribution @echo "(If no 'build' module, install with 'python -m pip install build setuptools_scm')" python -m build publish: @echo "Publish to PyPI at https://pypi.python.org/pypi/asttokens/" @VER=`python -c 'import setuptools_scm; print(setuptools_scm.get_version())'`; \ echo "Version in setup.py is $$VER"; \ echo "Git tag is `git describe --tags`"; \ echo "Run this manually: twine upload dist/asttokens-$$VER*" docs: @echo Build documentation in docs/_build/html source env/bin/activate ; PYTHONPATH=$(abspath .) $(MAKE) -C docs html clean: python setup.py clean source env/bin/activate ; PYTHONPATH=$(abspath .) $(MAKE) -C docs clean .PHONY: dist publish docs clean ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4884443 asttokens-3.0.0/PKG-INFO0000644000076700000240000001116614722510216013653 0ustar00develstaffMetadata-Version: 2.1 Name: asttokens Version: 3.0.0 Summary: Annotate AST trees with source code positions Home-page: https://github.com/gristlabs/asttokens Author: Dmitry Sagalovskiy, Grist Labs Author-email: dmitry@getgrist.com License: Apache 2.0 Keywords: code,ast,parse,tokenize,refactor Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Code Generators Classifier: Topic :: Software Development :: Compilers Classifier: Topic :: Software Development :: Interpreters Classifier: Topic :: Software Development :: Pre-processors Classifier: Environment :: Console Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Requires-Python: >=3.8 License-File: LICENSE Provides-Extra: astroid Requires-Dist: astroid<4,>=2; extra == "astroid" Provides-Extra: test Requires-Dist: astroid<4,>=2; extra == "test" Requires-Dist: pytest; extra == "test" Requires-Dist: pytest-cov; extra == "test" Requires-Dist: pytest-xdist; extra == "test" ASTTokens ========= .. image:: https://img.shields.io/pypi/v/asttokens.svg :target: https://pypi.python.org/pypi/asttokens/ .. image:: https://img.shields.io/pypi/pyversions/asttokens.svg :target: https://pypi.python.org/pypi/asttokens/ .. image:: https://github.com/gristlabs/asttokens/actions/workflows/build-and-test.yml/badge.svg :target: https://github.com/gristlabs/asttokens/actions/workflows/build-and-test.yml .. image:: https://readthedocs.org/projects/asttokens/badge/?version=latest :target: http://asttokens.readthedocs.io/en/latest/index.html .. image:: https://coveralls.io/repos/github/gristlabs/asttokens/badge.svg :target: https://coveralls.io/github/gristlabs/asttokens .. Start of user-guide The ``asttokens`` module annotates Python abstract syntax trees (ASTs) with the positions of tokens and text in the source code that generated them. It makes it possible for tools that work with logical AST nodes to find the particular text that resulted in those nodes, for example for automated refactoring or highlighting. Installation ------------ asttokens is available on PyPI: https://pypi.python.org/pypi/asttokens/:: pip install asttokens The code is on GitHub: https://github.com/gristlabs/asttokens. The API Reference is here: http://asttokens.readthedocs.io/en/latest/api-index.html. Usage ----- ASTTokens can annotate both trees built by `ast `_, AND those built by `astroid `_. Here's an example: .. code-block:: python import asttokens, ast source = "Robot('blue').walk(steps=10*n)" atok = asttokens.ASTTokens(source, parse=True) Once the tree has been marked, nodes get ``.first_token``, ``.last_token`` attributes, and the ``ASTTokens`` object offers helpful methods: .. code-block:: python attr_node = next(n for n in ast.walk(atok.tree) if isinstance(n, ast.Attribute)) print(atok.get_text(attr_node)) start, end = attr_node.last_token.startpos, attr_node.last_token.endpos print(atok.text[:start] + 'RUN' + atok.text[end:]) Which produces this output: .. code-block:: text Robot('blue').walk Robot('blue').RUN(steps=10*n) The ``ASTTokens`` object also offers methods to walk and search the list of tokens that make up the code (or a particular AST node), which is more useful and powerful than dealing with the text directly. Contribute ---------- To contribute: 1. Fork this repository, and clone your fork. 2. Install the package with test dependencies (ideally in a virtualenv) with:: pip install -e '.[test]' 3. Run tests in your current interpreter with the command ``pytest`` or ``python -m pytest``. 4. Run tests across all supported interpreters with the ``tox`` command. You will need to have the interpreters installed separately. We recommend ``pyenv`` for that. Use ``tox -p auto`` to run the tests in parallel. 5. By default certain tests which take a very long time to run are skipped, but they are run in CI. These are marked using the ``pytest`` marker ``slow`` and can be run on their own with ``pytest -m slow`` or as part of the full suite with ``pytest -m ''``. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/README.rst0000644000076700000240000000616114722467250014255 0ustar00develstaffASTTokens ========= .. image:: https://img.shields.io/pypi/v/asttokens.svg :target: https://pypi.python.org/pypi/asttokens/ .. image:: https://img.shields.io/pypi/pyversions/asttokens.svg :target: https://pypi.python.org/pypi/asttokens/ .. image:: https://github.com/gristlabs/asttokens/actions/workflows/build-and-test.yml/badge.svg :target: https://github.com/gristlabs/asttokens/actions/workflows/build-and-test.yml .. image:: https://readthedocs.org/projects/asttokens/badge/?version=latest :target: http://asttokens.readthedocs.io/en/latest/index.html .. image:: https://coveralls.io/repos/github/gristlabs/asttokens/badge.svg :target: https://coveralls.io/github/gristlabs/asttokens .. Start of user-guide The ``asttokens`` module annotates Python abstract syntax trees (ASTs) with the positions of tokens and text in the source code that generated them. It makes it possible for tools that work with logical AST nodes to find the particular text that resulted in those nodes, for example for automated refactoring or highlighting. Installation ------------ asttokens is available on PyPI: https://pypi.python.org/pypi/asttokens/:: pip install asttokens The code is on GitHub: https://github.com/gristlabs/asttokens. The API Reference is here: http://asttokens.readthedocs.io/en/latest/api-index.html. Usage ----- ASTTokens can annotate both trees built by `ast `_, AND those built by `astroid `_. Here's an example: .. code-block:: python import asttokens, ast source = "Robot('blue').walk(steps=10*n)" atok = asttokens.ASTTokens(source, parse=True) Once the tree has been marked, nodes get ``.first_token``, ``.last_token`` attributes, and the ``ASTTokens`` object offers helpful methods: .. code-block:: python attr_node = next(n for n in ast.walk(atok.tree) if isinstance(n, ast.Attribute)) print(atok.get_text(attr_node)) start, end = attr_node.last_token.startpos, attr_node.last_token.endpos print(atok.text[:start] + 'RUN' + atok.text[end:]) Which produces this output: .. code-block:: text Robot('blue').walk Robot('blue').RUN(steps=10*n) The ``ASTTokens`` object also offers methods to walk and search the list of tokens that make up the code (or a particular AST node), which is more useful and powerful than dealing with the text directly. Contribute ---------- To contribute: 1. Fork this repository, and clone your fork. 2. Install the package with test dependencies (ideally in a virtualenv) with:: pip install -e '.[test]' 3. Run tests in your current interpreter with the command ``pytest`` or ``python -m pytest``. 4. Run tests across all supported interpreters with the ``tox`` command. You will need to have the interpreters installed separately. We recommend ``pyenv`` for that. Use ``tox -p auto`` to run the tests in parallel. 5. By default certain tests which take a very long time to run are skipped, but they are run in CI. These are marked using the ``pytest`` marker ``slow`` and can be run on their own with ``pytest -m slow`` or as part of the full suite with ``pytest -m ''``. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4702334 asttokens-3.0.0/asttokens/0000755000076700000240000000000014722510216014564 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1667141924.0 asttokens-3.0.0/asttokens/__init__.py0000644000076700000240000000170214327510444016701 0ustar00develstaff# Copyright 2016 Grist Labs, Inc. # # 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. """ This module enhances the Python AST tree with token and source code information, sufficent to detect the source text of each AST node. This is helpful for tools that make source code transformations. """ from .line_numbers import LineNumbers from .asttokens import ASTText, ASTTokens, supports_tokenless __all__ = ['ASTText', 'ASTTokens', 'LineNumbers', 'supports_tokenless'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710615991.0 asttokens-3.0.0/asttokens/astroid_compat.py0000644000076700000240000000111214575366667020172 0ustar00develstafftry: from astroid import nodes as astroid_node_classes # astroid_node_classes should be whichever module has the NodeNG class from astroid.nodes import NodeNG from astroid.nodes import BaseContainer except Exception: try: from astroid import node_classes as astroid_node_classes from astroid.node_classes import NodeNG from astroid.node_classes import _BaseContainer as BaseContainer except Exception: # pragma: no cover astroid_node_classes = None NodeNG = None BaseContainer = None __all__ = ["astroid_node_classes", "NodeNG", "BaseContainer"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/asttokens/asttokens.py0000644000076700000240000004254014722467250017167 0ustar00develstaff# Copyright 2016 Grist Labs, Inc. # # 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. import abc import ast import bisect import sys import token from ast import Module from typing import Iterable, Iterator, List, Optional, Tuple, Any, cast, TYPE_CHECKING from .line_numbers import LineNumbers from .util import ( Token, match_token, is_non_coding_token, patched_generate_tokens, last_stmt, annotate_fstring_nodes, generate_tokens, is_module, is_stmt ) if TYPE_CHECKING: # pragma: no cover from .util import AstNode, TokenInfo class ASTTextBase(metaclass=abc.ABCMeta): def __init__(self, source_text: str, filename: str) -> None: self._filename = filename # Decode source after parsing to let Python 2 handle coding declarations. # (If the encoding was not utf-8 compatible, then even if it parses correctly, # we'll fail with a unicode error here.) source_text = str(source_text) self._text = source_text self._line_numbers = LineNumbers(source_text) @abc.abstractmethod def get_text_positions(self, node, padded): # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] """ Returns two ``(lineno, col_offset)`` tuples for the start and end of the given node. If the positions can't be determined, or the nodes don't correspond to any particular text, returns ``(1, 0)`` for both. ``padded`` corresponds to the ``padded`` argument to ``ast.get_source_segment()``. This means that if ``padded`` is True, the start position will be adjusted to include leading whitespace if ``node`` is a multiline statement. """ raise NotImplementedError # pragma: no cover def get_text_range(self, node, padded=True): # type: (AstNode, bool) -> Tuple[int, int] """ Returns the (startpos, endpos) positions in source text corresponding to the given node. Returns (0, 0) for nodes (like `Load`) that don't correspond to any particular text. See ``get_text_positions()`` for details on the ``padded`` argument. """ start, end = self.get_text_positions(node, padded) return ( self._line_numbers.line_to_offset(*start), self._line_numbers.line_to_offset(*end), ) def get_text(self, node, padded=True): # type: (AstNode, bool) -> str """ Returns the text corresponding to the given node. Returns '' for nodes (like `Load`) that don't correspond to any particular text. See ``get_text_positions()`` for details on the ``padded`` argument. """ start, end = self.get_text_range(node, padded) return self._text[start: end] class ASTTokens(ASTTextBase): """ ASTTokens maintains the text of Python code in several forms: as a string, as line numbers, and as tokens, and is used to mark and access token and position information. ``source_text`` must be a unicode or UTF8-encoded string. If you pass in UTF8 bytes, remember that all offsets you'll get are to the unicode text, which is available as the ``.text`` property. If ``parse`` is set, the ``source_text`` will be parsed with ``ast.parse()``, and the resulting tree marked with token info and made available as the ``.tree`` property. If ``tree`` is given, it will be marked and made available as the ``.tree`` property. In addition to the trees produced by the ``ast`` module, ASTTokens will also mark trees produced using ``astroid`` library . If only ``source_text`` is given, you may use ``.mark_tokens(tree)`` to mark the nodes of an AST tree created separately. """ def __init__(self, source_text, parse=False, tree=None, filename='', tokens=None): # type: (Any, bool, Optional[Module], str, Iterable[TokenInfo]) -> None super(ASTTokens, self).__init__(source_text, filename) self._tree = ast.parse(source_text, filename) if parse else tree # Tokenize the code. if tokens is None: tokens = generate_tokens(self._text) self._tokens = list(self._translate_tokens(tokens)) # Extract the start positions of all tokens, so that we can quickly map positions to tokens. self._token_offsets = [tok.startpos for tok in self._tokens] if self._tree: self.mark_tokens(self._tree) def mark_tokens(self, root_node): # type: (Module) -> None """ Given the root of the AST or Astroid tree produced from source_text, visits all nodes marking them with token and position information by adding ``.first_token`` and ``.last_token`` attributes. This is done automatically in the constructor when ``parse`` or ``tree`` arguments are set, but may be used manually with a separate AST or Astroid tree. """ # The hard work of this class is done by MarkTokens from .mark_tokens import MarkTokens # to avoid import loops MarkTokens(self).visit_tree(root_node) def _translate_tokens(self, original_tokens): # type: (Iterable[TokenInfo]) -> Iterator[Token] """ Translates the given standard library tokens into our own representation. """ for index, tok in enumerate(patched_generate_tokens(original_tokens)): tok_type, tok_str, start, end, line = tok yield Token(tok_type, tok_str, start, end, line, index, self._line_numbers.line_to_offset(start[0], start[1]), self._line_numbers.line_to_offset(end[0], end[1])) @property def text(self): # type: () -> str """The source code passed into the constructor.""" return self._text @property def tokens(self): # type: () -> List[Token] """The list of tokens corresponding to the source code from the constructor.""" return self._tokens @property def tree(self): # type: () -> Optional[Module] """The root of the AST tree passed into the constructor or parsed from the source code.""" return self._tree @property def filename(self): # type: () -> str """The filename that was parsed""" return self._filename def get_token_from_offset(self, offset): # type: (int) -> Token """ Returns the token containing the given character offset (0-based position in source text), or the preceeding token if the position is between tokens. """ return self._tokens[bisect.bisect(self._token_offsets, offset) - 1] def get_token(self, lineno, col_offset): # type: (int, int) -> Token """ Returns the token containing the given (lineno, col_offset) position, or the preceeding token if the position is between tokens. """ # TODO: add test for multibyte unicode. We need to translate offsets from ast module (which # are in utf8) to offsets into the unicode text. tokenize module seems to use unicode offsets # but isn't explicit. return self.get_token_from_offset(self._line_numbers.line_to_offset(lineno, col_offset)) def get_token_from_utf8(self, lineno, col_offset): # type: (int, int) -> Token """ Same as get_token(), but interprets col_offset as a UTF8 offset, which is what `ast` uses. """ return self.get_token(lineno, self._line_numbers.from_utf8_col(lineno, col_offset)) def next_token(self, tok, include_extra=False): # type: (Token, bool) -> Token """ Returns the next token after the given one. If include_extra is True, includes non-coding tokens from the tokenize module, such as NL and COMMENT. """ i = tok.index + 1 if not include_extra: while is_non_coding_token(self._tokens[i].type): i += 1 return self._tokens[i] def prev_token(self, tok, include_extra=False): # type: (Token, bool) -> Token """ Returns the previous token before the given one. If include_extra is True, includes non-coding tokens from the tokenize module, such as NL and COMMENT. """ i = tok.index - 1 if not include_extra: while is_non_coding_token(self._tokens[i].type): i -= 1 return self._tokens[i] def find_token(self, start_token, tok_type, tok_str=None, reverse=False): # type: (Token, int, Optional[str], bool) -> Token """ Looks for the first token, starting at start_token, that matches tok_type and, if given, the token string. Searches backwards if reverse is True. Returns ENDMARKER token if not found (you can check it with `token.ISEOF(t.type)`). """ t = start_token advance = self.prev_token if reverse else self.next_token while not match_token(t, tok_type, tok_str) and not token.ISEOF(t.type): t = advance(t, include_extra=True) return t def token_range(self, first_token, # type: Token last_token, # type: Token include_extra=False, # type: bool ): # type: (...) -> Iterator[Token] """ Yields all tokens in order from first_token through and including last_token. If include_extra is True, includes non-coding tokens such as tokenize.NL and .COMMENT. """ for i in range(first_token.index, last_token.index + 1): if include_extra or not is_non_coding_token(self._tokens[i].type): yield self._tokens[i] def get_tokens(self, node, include_extra=False): # type: (AstNode, bool) -> Iterator[Token] """ Yields all tokens making up the given node. If include_extra is True, includes non-coding tokens such as tokenize.NL and .COMMENT. """ return self.token_range(node.first_token, node.last_token, include_extra=include_extra) def get_text_positions(self, node, padded): # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] """ Returns two ``(lineno, col_offset)`` tuples for the start and end of the given node. If the positions can't be determined, or the nodes don't correspond to any particular text, returns ``(1, 0)`` for both. ``padded`` corresponds to the ``padded`` argument to ``ast.get_source_segment()``. This means that if ``padded`` is True, the start position will be adjusted to include leading whitespace if ``node`` is a multiline statement. """ if not hasattr(node, 'first_token'): return (1, 0), (1, 0) start = node.first_token.start end = node.last_token.end if padded and any(match_token(t, token.NEWLINE) for t in self.get_tokens(node)): # Set col_offset to 0 to include leading indentation for multiline statements. start = (start[0], 0) return start, end class ASTText(ASTTextBase): """ Supports the same ``get_text*`` methods as ``ASTTokens``, but uses the AST to determine the text positions instead of tokens. This is faster than ``ASTTokens`` as it requires less setup work. It also (sometimes) supports nodes inside f-strings, which ``ASTTokens`` doesn't. Some node types and/or Python versions are not supported. In these cases the ``get_text*`` methods will fall back to using ``ASTTokens`` which incurs the usual setup cost the first time. If you want to avoid this, check ``supports_tokenless(node)`` before calling ``get_text*`` methods. """ def __init__(self, source_text, tree=None, filename=''): # type: (Any, Optional[Module], str) -> None super(ASTText, self).__init__(source_text, filename) self._tree = tree if self._tree is not None: annotate_fstring_nodes(self._tree) self._asttokens = None # type: Optional[ASTTokens] @property def tree(self): # type: () -> Module if self._tree is None: self._tree = ast.parse(self._text, self._filename) annotate_fstring_nodes(self._tree) return self._tree @property def asttokens(self): # type: () -> ASTTokens if self._asttokens is None: self._asttokens = ASTTokens( self._text, tree=self.tree, filename=self._filename, ) return self._asttokens def _get_text_positions_tokenless(self, node, padded): # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] """ Version of ``get_text_positions()`` that doesn't use tokens. """ if is_module(node): # Modules don't have position info, so just return the range of the whole text. # The token-using method does something different, but its behavior seems weird and inconsistent. # For example, in a file with only comments, it only returns the first line. # It's hard to imagine a case when this matters. return (1, 0), self._line_numbers.offset_to_line(len(self._text)) if getattr(node, 'lineno', None) is None: return (1, 0), (1, 0) assert node # tell mypy that node is not None, which we allowed up to here for compatibility decorators = getattr(node, 'decorator_list', []) if not decorators: # Astroid uses node.decorators.nodes instead of node.decorator_list. decorators_node = getattr(node, 'decorators', None) decorators = getattr(decorators_node, 'nodes', []) if decorators: # Function/Class definition nodes are marked by AST as starting at def/class, # not the first decorator. This doesn't match the token-using behavior, # or inspect.getsource(), and just seems weird. start_node = decorators[0] else: start_node = node start_lineno = start_node.lineno end_node = last_stmt(node) # Include leading indentation for multiline statements. # This doesn't mean simple statements that happen to be on multiple lines, # but compound statements where inner indentation matters. # So we don't just compare node.lineno and node.end_lineno, # we check for a contained statement starting on a different line. if padded and ( start_lineno != end_node.lineno or ( # Astroid docstrings aren't treated as separate statements. # So to handle function/class definitions with a docstring but no other body, # we just check that the node is a statement with a docstring # and spanning multiple lines in the simple, literal sense. start_lineno != node.end_lineno and getattr(node, "doc_node", None) and is_stmt(node) ) ): start_col_offset = 0 else: start_col_offset = self._line_numbers.from_utf8_col(start_lineno, start_node.col_offset) start = (start_lineno, start_col_offset) # To match the token-using behaviour, we exclude trailing semicolons and comments. # This means that for blocks containing multiple statements, we have to use the last one # instead of the actual node for end_lineno and end_col_offset. end_lineno = cast(int, end_node.end_lineno) end_col_offset = cast(int, end_node.end_col_offset) end_col_offset = self._line_numbers.from_utf8_col(end_lineno, end_col_offset) end = (end_lineno, end_col_offset) return start, end def get_text_positions(self, node, padded): # type: (AstNode, bool) -> Tuple[Tuple[int, int], Tuple[int, int]] """ Returns two ``(lineno, col_offset)`` tuples for the start and end of the given node. If the positions can't be determined, or the nodes don't correspond to any particular text, returns ``(1, 0)`` for both. ``padded`` corresponds to the ``padded`` argument to ``ast.get_source_segment()``. This means that if ``padded`` is True, the start position will be adjusted to include leading whitespace if ``node`` is a multiline statement. """ if getattr(node, "_broken_positions", None): # This node was marked in util.annotate_fstring_nodes as having untrustworthy lineno/col_offset. return (1, 0), (1, 0) if supports_tokenless(node): return self._get_text_positions_tokenless(node, padded) return self.asttokens.get_text_positions(node, padded) # Node types that _get_text_positions_tokenless doesn't support. # These initial values are missing lineno. _unsupported_tokenless_types = ("arguments", "Arguments", "withitem") # type: Tuple[str, ...] if sys.version_info[:2] == (3, 8): # _get_text_positions_tokenless works incorrectly for these types due to bugs in Python 3.8. _unsupported_tokenless_types += ("arg", "Starred") # no lineno in 3.8 _unsupported_tokenless_types += ("Slice", "ExtSlice", "Index", "keyword") def supports_tokenless(node=None): # type: (Any) -> bool """ Returns True if the Python version and the node (if given) are supported by the ``get_text*`` methods of ``ASTText`` without falling back to ``ASTTokens``. See ``ASTText`` for why this matters. The following cases are not supported: - PyPy - ``ast.arguments`` / ``astroid.Arguments`` - ``ast.withitem`` - ``astroid.Comprehension`` - ``astroid.AssignName`` inside ``astroid.Arguments`` or ``astroid.ExceptHandler`` - The following nodes in Python 3.8 only: - ``ast.arg`` - ``ast.Starred`` - ``ast.Slice`` - ``ast.ExtSlice`` - ``ast.Index`` - ``ast.keyword`` """ return ( type(node).__name__ not in _unsupported_tokenless_types and not ( # astroid nodes not isinstance(node, ast.AST) and node is not None and ( ( type(node).__name__ == "AssignName" and type(node.parent).__name__ in ("Arguments", "ExceptHandler") ) ) ) and 'pypy' not in sys.version.lower() ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/asttokens/line_numbers.py0000644000076700000240000000542214722467250017634 0ustar00develstaff# Copyright 2016 Grist Labs, Inc. # # 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. import bisect import re from typing import Dict, List, Tuple _line_start_re = re.compile(r'^', re.M) class LineNumbers: """ Class to convert between character offsets in a text string, and pairs (line, column) of 1-based line and 0-based column numbers, as used by tokens and AST nodes. This class expects unicode for input and stores positions in unicode. But it supports translating to and from utf8 offsets, which are used by ast parsing. """ def __init__(self, text): # type: (str) -> None # A list of character offsets of each line's first character. self._line_offsets = [m.start(0) for m in _line_start_re.finditer(text)] self._text = text self._text_len = len(text) self._utf8_offset_cache = {} # type: Dict[int, List[int]] # maps line num to list of char offset for each byte in line def from_utf8_col(self, line, utf8_column): # type: (int, int) -> int """ Given a 1-based line number and 0-based utf8 column, returns a 0-based unicode column. """ offsets = self._utf8_offset_cache.get(line) if offsets is None: end_offset = self._line_offsets[line] if line < len(self._line_offsets) else self._text_len line_text = self._text[self._line_offsets[line - 1] : end_offset] offsets = [i for i,c in enumerate(line_text) for byte in c.encode('utf8')] offsets.append(len(line_text)) self._utf8_offset_cache[line] = offsets return offsets[max(0, min(len(offsets)-1, utf8_column))] def line_to_offset(self, line, column): # type: (int, int) -> int """ Converts 1-based line number and 0-based column to 0-based character offset into text. """ line -= 1 if line >= len(self._line_offsets): return self._text_len elif line < 0: return 0 else: return min(self._line_offsets[line] + max(0, column), self._text_len) def offset_to_line(self, offset): # type: (int) -> Tuple[int, int] """ Converts 0-based character offset to pair (line, col) of 1-based line and 0-based column numbers. """ offset = max(0, min(self._text_len, offset)) line_index = bisect.bisect_right(self._line_offsets, offset) - 1 return (line_index + 1, offset - self._line_offsets[line_index]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732933805.0 asttokens-3.0.0/asttokens/mark_tokens.py0000644000076700000240000005102414722474255017471 0ustar00develstaff# Copyright 2016 Grist Labs, Inc. # # 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. import ast import numbers import sys import token from ast import Module from typing import Callable, List, Union, cast, Optional, Tuple, TYPE_CHECKING from . import util from .asttokens import ASTTokens from .astroid_compat import astroid_node_classes as nc, BaseContainer as AstroidBaseContainer if TYPE_CHECKING: from .util import AstNode # Mapping of matching braces. To find a token here, look up token[:2]. _matching_pairs_left = { (token.OP, '('): (token.OP, ')'), (token.OP, '['): (token.OP, ']'), (token.OP, '{'): (token.OP, '}'), } _matching_pairs_right = { (token.OP, ')'): (token.OP, '('), (token.OP, ']'): (token.OP, '['), (token.OP, '}'): (token.OP, '{'), } class MarkTokens: """ Helper that visits all nodes in the AST tree and assigns .first_token and .last_token attributes to each of them. This is the heart of the token-marking logic. """ def __init__(self, code): # type: (ASTTokens) -> None self._code = code self._methods = util.NodeMethods() self._iter_children = None # type: Optional[Callable] def visit_tree(self, node): # type: (Module) -> None self._iter_children = util.iter_children_func(node) util.visit_tree(node, self._visit_before_children, self._visit_after_children) def _visit_before_children(self, node, parent_token): # type: (AstNode, Optional[util.Token]) -> Tuple[Optional[util.Token], Optional[util.Token]] col = getattr(node, 'col_offset', None) token = self._code.get_token_from_utf8(node.lineno, col) if col is not None else None if not token and util.is_module(node): # We'll assume that a Module node starts at the start of the source code. token = self._code.get_token(1, 0) # Use our own token, or our parent's if we don't have one, to pass to child calls as # parent_token argument. The second value becomes the token argument of _visit_after_children. return (token or parent_token, token) def _visit_after_children(self, node, parent_token, token): # type: (AstNode, Optional[util.Token], Optional[util.Token]) -> None # This processes the node generically first, after all children have been processed. # Get the first and last tokens that belong to children. Note how this doesn't assume that we # iterate through children in order that corresponds to occurrence in source code. This # assumption can fail (e.g. with return annotations). first = token last = None for child in cast(Callable, self._iter_children)(node): # astroid slices have especially wrong positions, we don't want them to corrupt their parents. if util.is_empty_astroid_slice(child): continue if not first or child.first_token.index < first.index: first = child.first_token if not last or child.last_token.index > last.index: last = child.last_token # If we don't have a first token from _visit_before_children, and there were no children, then # use the parent's token as the first token. first = first or parent_token # If no children, set last token to the first one. last = last or first # Statements continue to before NEWLINE. This helps cover a few different cases at once. if util.is_stmt(node): last = self._find_last_in_stmt(cast(util.Token, last)) # Capture any unmatched brackets. first, last = self._expand_to_matching_pairs(cast(util.Token, first), cast(util.Token, last), node) # Give a chance to node-specific methods to adjust. nfirst, nlast = self._methods.get(self, node.__class__)(node, first, last) if (nfirst, nlast) != (first, last): # If anything changed, expand again to capture any unmatched brackets. nfirst, nlast = self._expand_to_matching_pairs(nfirst, nlast, node) node.first_token = nfirst node.last_token = nlast def _find_last_in_stmt(self, start_token): # type: (util.Token) -> util.Token t = start_token while (not util.match_token(t, token.NEWLINE) and not util.match_token(t, token.OP, ';') and not token.ISEOF(t.type)): t = self._code.next_token(t, include_extra=True) return self._code.prev_token(t) def _expand_to_matching_pairs(self, first_token, last_token, node): # type: (util.Token, util.Token, AstNode) -> Tuple[util.Token, util.Token] """ Scan tokens in [first_token, last_token] range that are between node's children, and for any unmatched brackets, adjust first/last tokens to include the closing pair. """ # We look for opening parens/braces among non-child tokens (i.e. tokens between our actual # child nodes). If we find any closing ones, we match them to the opens. to_match_right = [] # type: List[Tuple[int, str]] to_match_left = [] for tok in self._code.token_range(first_token, last_token): tok_info = tok[:2] if to_match_right and tok_info == to_match_right[-1]: to_match_right.pop() elif tok_info in _matching_pairs_left: to_match_right.append(_matching_pairs_left[tok_info]) elif tok_info in _matching_pairs_right: to_match_left.append(_matching_pairs_right[tok_info]) # Once done, extend `last_token` to match any unclosed parens/braces. for match in reversed(to_match_right): last = self._code.next_token(last_token) # Allow for trailing commas or colons (allowed in subscripts) before the closing delimiter while any(util.match_token(last, token.OP, x) for x in (',', ':')): last = self._code.next_token(last) # Now check for the actual closing delimiter. if util.match_token(last, *match): last_token = last # And extend `first_token` to match any unclosed opening parens/braces. for match in to_match_left: first = self._code.prev_token(first_token) if util.match_token(first, *match): first_token = first return (first_token, last_token) #---------------------------------------------------------------------- # Node visitors. Each takes a preliminary first and last tokens, and returns the adjusted pair # that will actually be assigned. def visit_default(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # pylint: disable=no-self-use # By default, we don't need to adjust the token we computed earlier. return (first_token, last_token) def handle_comp(self, open_brace, node, first_token, last_token): # type: (str, AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # For list/set/dict comprehensions, we only get the token of the first child, so adjust it to # include the opening brace (the closing brace will be matched automatically). before = self._code.prev_token(first_token) util.expect_token(before, token.OP, open_brace) return (before, last_token) def visit_comprehension(self, node, # type: AstNode first_token, # type: util.Token last_token, # type: util.Token ): # type: (...) -> Tuple[util.Token, util.Token] # The 'comprehension' node starts with 'for' but we only get first child; we search backwards # to find the 'for' keyword. first = self._code.find_token(first_token, token.NAME, 'for', reverse=True) return (first, last_token) def visit_if(self, node, first_token, last_token): # type: (util.Token, util.Token, util.Token) -> Tuple[util.Token, util.Token] while first_token.string not in ('if', 'elif'): first_token = self._code.prev_token(first_token) return first_token, last_token def handle_attr(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # Attribute node has ".attr" (2 tokens) after the last child. dot = self._code.find_token(last_token, token.OP, '.') name = self._code.next_token(dot) util.expect_token(name, token.NAME) return (first_token, name) visit_attribute = handle_attr visit_assignattr = handle_attr visit_delattr = handle_attr def handle_def(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # With astroid, nodes that start with a doc-string can have an empty body, in which case we # need to adjust the last token to include the doc string. if not node.body and (getattr(node, 'doc_node', None) or getattr(node, 'doc', None)): # type: ignore[union-attr] last_token = self._code.find_token(last_token, token.STRING) # Include @ from decorator if first_token.index > 0: prev = self._code.prev_token(first_token) if util.match_token(prev, token.OP, '@'): first_token = prev return (first_token, last_token) visit_classdef = handle_def visit_functiondef = handle_def def handle_following_brackets(self, node, last_token, opening_bracket): # type: (AstNode, util.Token, str) -> util.Token # This is for calls and subscripts, which have a pair of brackets # at the end which may contain no nodes, e.g. foo() or bar[:]. # We look for the opening bracket and then let the matching pair be found automatically # Remember that last_token is at the end of all children, # so we are not worried about encountering a bracket that belongs to a child. first_child = next(cast(Callable, self._iter_children)(node)) call_start = self._code.find_token(first_child.last_token, token.OP, opening_bracket) if call_start.index > last_token.index: last_token = call_start return last_token def visit_call(self, node, first_token, last_token): # type: (util.Token, util.Token, util.Token) -> Tuple[util.Token, util.Token] last_token = self.handle_following_brackets(node, last_token, '(') # Handling a python bug with decorators with empty parens, e.g. # @deco() # def ... if util.match_token(first_token, token.OP, '@'): first_token = self._code.next_token(first_token) return (first_token, last_token) def visit_matchclass(self, node, first_token, last_token): # type: (util.Token, util.Token, util.Token) -> Tuple[util.Token, util.Token] last_token = self.handle_following_brackets(node, last_token, '(') return (first_token, last_token) def visit_subscript(self, node, # type: AstNode first_token, # type: util.Token last_token, # type: util.Token ): # type: (...) -> Tuple[util.Token, util.Token] last_token = self.handle_following_brackets(node, last_token, '[') return (first_token, last_token) def visit_slice(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # consume `:` tokens to the left and right. In Python 3.9, Slice nodes are # given a col_offset, (and end_col_offset), so this will always start inside # the slice, even if it is the empty slice. However, in 3.8 and below, this # will only expand to the full slice if the slice contains a node with a # col_offset. So x[:] will only get the correct tokens in 3.9, but x[1:] and # x[:1] will even on earlier versions of Python. while True: prev = self._code.prev_token(first_token) if prev.string != ':': break first_token = prev while True: next_ = self._code.next_token(last_token) if next_.string != ':': break last_token = next_ return (first_token, last_token) def handle_bare_tuple(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # A bare tuple doesn't include parens; if there is a trailing comma, make it part of the tuple. maybe_comma = self._code.next_token(last_token) if util.match_token(maybe_comma, token.OP, ','): last_token = maybe_comma return (first_token, last_token) # In Python3.8 parsed tuples include parentheses when present. def handle_tuple_nonempty(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] assert isinstance(node, ast.Tuple) or isinstance(node, AstroidBaseContainer) # It's a bare tuple if the first token belongs to the first child. The first child may # include extraneous parentheses (which don't create new nodes), so account for those too. child = node.elts[0] if TYPE_CHECKING: child = cast(AstNode, child) child_first, child_last = self._gobble_parens(child.first_token, child.last_token, True) if first_token == child_first: return self.handle_bare_tuple(node, first_token, last_token) return (first_token, last_token) def visit_tuple(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] assert isinstance(node, ast.Tuple) or isinstance(node, AstroidBaseContainer) if not node.elts: # An empty tuple is just "()", and we need no further info. return (first_token, last_token) return self.handle_tuple_nonempty(node, first_token, last_token) def _gobble_parens(self, first_token, last_token, include_all=False): # type: (util.Token, util.Token, bool) -> Tuple[util.Token, util.Token] # Expands a range of tokens to include one or all pairs of surrounding parentheses, and # returns (first, last) tokens that include these parens. while first_token.index > 0: prev = self._code.prev_token(first_token) next = self._code.next_token(last_token) if util.match_token(prev, token.OP, '(') and util.match_token(next, token.OP, ')'): first_token, last_token = prev, next if include_all: continue break return (first_token, last_token) def visit_str(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] return self.handle_str(first_token, last_token) def visit_joinedstr(self, node, # type: AstNode first_token, # type: util.Token last_token, # type: util.Token ): # type: (...) -> Tuple[util.Token, util.Token] if sys.version_info < (3, 12): # Older versions don't tokenize the contents of f-strings return self.handle_str(first_token, last_token) last = first_token while True: if util.match_token(last, getattr(token, "FSTRING_START")): # Python 3.12+ has tokens for the start (e.g. `f"`) and end (`"`) # of the f-string. We can't just look for the next FSTRING_END # because f-strings can be nested, e.g. f"{f'{x}'}", so we need # to treat this like matching balanced parentheses. count = 1 while count > 0: last = self._code.next_token(last) # mypy complains about token.FSTRING_START and token.FSTRING_END. if util.match_token(last, getattr(token, "FSTRING_START")): count += 1 elif util.match_token(last, getattr(token, "FSTRING_END")): count -= 1 last_token = last last = self._code.next_token(last_token) elif util.match_token(last, token.STRING): # Similar to handle_str, we also need to handle adjacent strings. last_token = last last = self._code.next_token(last_token) else: break return (first_token, last_token) def visit_bytes(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] return self.handle_str(first_token, last_token) def handle_str(self, first_token, last_token): # type: (util.Token, util.Token) -> Tuple[util.Token, util.Token] # Multiple adjacent STRING tokens form a single string. last = self._code.next_token(last_token) while util.match_token(last, token.STRING): last_token = last last = self._code.next_token(last_token) return (first_token, last_token) def handle_num(self, node, # type: AstNode value, # type: Union[complex, int, numbers.Number] first_token, # type: util.Token last_token, # type: util.Token ): # type: (...) -> Tuple[util.Token, util.Token] # A constant like '-1' gets turned into two tokens; this will skip the '-'. while util.match_token(last_token, token.OP): last_token = self._code.next_token(last_token) if isinstance(value, complex): # A complex number like -2j cannot be compared directly to 0 # A complex number like 1-2j is expressed as a binary operation # so we don't need to worry about it value = value.imag # This makes sure that the - is included if value < 0 and first_token.type == token.NUMBER: # type: ignore[operator] first_token = self._code.prev_token(first_token) return (first_token, last_token) def visit_num(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] return self.handle_num(node, cast(ast.Num, node).n, first_token, last_token) def visit_const(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] assert isinstance(node, ast.Constant) or isinstance(node, nc.Const) if isinstance(node.value, numbers.Number): return self.handle_num(node, node.value, first_token, last_token) elif isinstance(node.value, (str, bytes)): return self.visit_str(node, first_token, last_token) return (first_token, last_token) visit_constant = visit_const def visit_keyword(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # Until python 3.9 (https://bugs.python.org/issue40141), # ast.keyword nodes didn't have line info. Astroid has lineno None. assert isinstance(node, ast.keyword) or isinstance(node, nc.Keyword) if node.arg is not None and getattr(node, 'lineno', None) is None: equals = self._code.find_token(first_token, token.OP, '=', reverse=True) name = self._code.prev_token(equals) util.expect_token(name, token.NAME, node.arg) first_token = name return (first_token, last_token) def visit_starred(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # Astroid has 'Starred' nodes (for "foo(*bar)" type args), but they need to be adjusted. if not util.match_token(first_token, token.OP, '*'): star = self._code.prev_token(first_token) if util.match_token(star, token.OP, '*'): first_token = star return (first_token, last_token) def visit_assignname(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] # Astroid may turn 'except' clause into AssignName, but we need to adjust it. if util.match_token(first_token, token.NAME, 'except'): colon = self._code.find_token(last_token, token.OP, ':') first_token = last_token = self._code.prev_token(colon) return (first_token, last_token) # Async nodes should typically start with the word 'async' # but Python < 3.7 doesn't put the col_offset there # AsyncFunctionDef is slightly different because it might have # decorators before that, which visit_functiondef handles def handle_async(self, node, first_token, last_token): # type: (AstNode, util.Token, util.Token) -> Tuple[util.Token, util.Token] if not first_token.string == 'async': first_token = self._code.prev_token(first_token) return (first_token, last_token) visit_asyncfor = handle_async visit_asyncwith = handle_async def visit_asyncfunctiondef(self, node, # type: AstNode first_token, # type: util.Token last_token, # type: util.Token ): # type: (...) -> Tuple[util.Token, util.Token] if util.match_token(first_token, token.NAME, 'def'): # Include the 'async' token first_token = self._code.prev_token(first_token) return self.visit_functiondef(node, first_token, last_token) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1659412507.0 asttokens-3.0.0/asttokens/py.typed0000644000076700000240000000000014272120033016243 0ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/asttokens/util.py0000644000076700000240000004160614722467250016133 0ustar00develstaff# Copyright 2016 Grist Labs, Inc. # # 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. import ast import collections import io import sys import token import tokenize from abc import ABCMeta from ast import Module, expr, AST from functools import lru_cache from typing import ( Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union, cast, Any, TYPE_CHECKING, Type, ) if TYPE_CHECKING: # pragma: no cover from .astroid_compat import NodeNG # Type class used to expand out the definition of AST to include fields added by this library # It's not actually used for anything other than type checking though! class EnhancedAST(AST): # Additional attributes set by mark_tokens first_token = None # type: Token last_token = None # type: Token lineno = 0 # type: int AstNode = Union[EnhancedAST, NodeNG] TokenInfo = tokenize.TokenInfo def token_repr(tok_type, string): # type: (int, Optional[str]) -> str """Returns a human-friendly representation of a token with the given type and string.""" # repr() prefixes unicode with 'u' on Python2 but not Python3; strip it out for consistency. return '%s:%s' % (token.tok_name[tok_type], repr(string).lstrip('u')) class Token(collections.namedtuple('Token', 'type string start end line index startpos endpos')): """ TokenInfo is an 8-tuple containing the same 5 fields as the tokens produced by the tokenize module, and 3 additional ones useful for this module: - [0] .type Token type (see token.py) - [1] .string Token (a string) - [2] .start Starting (row, column) indices of the token (a 2-tuple of ints) - [3] .end Ending (row, column) indices of the token (a 2-tuple of ints) - [4] .line Original line (string) - [5] .index Index of the token in the list of tokens that it belongs to. - [6] .startpos Starting character offset into the input text. - [7] .endpos Ending character offset into the input text. """ def __str__(self): # type: () -> str return token_repr(self.type, self.string) def match_token(token, tok_type, tok_str=None): # type: (Token, int, Optional[str]) -> bool """Returns true if token is of the given type and, if a string is given, has that string.""" return token.type == tok_type and (tok_str is None or token.string == tok_str) def expect_token(token, tok_type, tok_str=None): # type: (Token, int, Optional[str]) -> None """ Verifies that the given token is of the expected type. If tok_str is given, the token string is verified too. If the token doesn't match, raises an informative ValueError. """ if not match_token(token, tok_type, tok_str): raise ValueError("Expected token %s, got %s on line %s col %s" % ( token_repr(tok_type, tok_str), str(token), token.start[0], token.start[1] + 1)) def is_non_coding_token(token_type): # type: (int) -> bool """ These are considered non-coding tokens, as they don't affect the syntax tree. """ return token_type in (token.NL, token.COMMENT, token.ENCODING) def generate_tokens(text): # type: (str) -> Iterator[TokenInfo] """ Generates standard library tokens for the given code. """ # tokenize.generate_tokens is technically an undocumented API for Python3, but allows us to use the same API as for # Python2. See http://stackoverflow.com/a/4952291/328565. # FIXME: Remove cast once https://github.com/python/typeshed/issues/7003 gets fixed return tokenize.generate_tokens(cast(Callable[[], str], io.StringIO(text).readline)) def iter_children_func(node): # type: (AST) -> Callable """ Returns a function which yields all direct children of a AST node, skipping children that are singleton nodes. The function depends on whether ``node`` is from ``ast`` or from the ``astroid`` module. """ return iter_children_astroid if hasattr(node, 'get_children') else iter_children_ast def iter_children_astroid(node, include_joined_str=False): # type: (NodeNG, bool) -> Union[Iterator, List] if not include_joined_str and is_joined_str(node): return [] return node.get_children() SINGLETONS = {c for n, c in ast.__dict__.items() if isinstance(c, type) and issubclass(c, (ast.expr_context, ast.boolop, ast.operator, ast.unaryop, ast.cmpop))} def iter_children_ast(node, include_joined_str=False): # type: (AST, bool) -> Iterator[Union[AST, expr]] if not include_joined_str and is_joined_str(node): return if isinstance(node, ast.Dict): # override the iteration order: instead of , , # yield keys and values in source order (key1, value1, key2, value2, ...) for (key, value) in zip(node.keys, node.values): if key is not None: yield key yield value return for child in ast.iter_child_nodes(node): # Skip singleton children; they don't reflect particular positions in the code and break the # assumptions about the tree consisting of distinct nodes. Note that collecting classes # beforehand and checking them in a set is faster than using isinstance each time. if child.__class__ not in SINGLETONS: yield child stmt_class_names = {n for n, c in ast.__dict__.items() if isinstance(c, type) and issubclass(c, ast.stmt)} expr_class_names = ({n for n, c in ast.__dict__.items() if isinstance(c, type) and issubclass(c, ast.expr)} | {'AssignName', 'DelName', 'Const', 'AssignAttr', 'DelAttr'}) # These feel hacky compared to isinstance() but allow us to work with both ast and astroid nodes # in the same way, and without even importing astroid. def is_expr(node): # type: (AstNode) -> bool """Returns whether node is an expression node.""" return node.__class__.__name__ in expr_class_names def is_stmt(node): # type: (AstNode) -> bool """Returns whether node is a statement node.""" return node.__class__.__name__ in stmt_class_names def is_module(node): # type: (AstNode) -> bool """Returns whether node is a module node.""" return node.__class__.__name__ == 'Module' def is_joined_str(node): # type: (AstNode) -> bool """Returns whether node is a JoinedStr node, used to represent f-strings.""" # At the moment, nodes below JoinedStr have wrong line/col info, and trying to process them only # leads to errors. return node.__class__.__name__ == 'JoinedStr' def is_expr_stmt(node): # type: (AstNode) -> bool """Returns whether node is an `Expr` node, which is a statement that is an expression.""" return node.__class__.__name__ == 'Expr' CONSTANT_CLASSES: Tuple[Type, ...] = (ast.Constant,) try: from astroid import Const CONSTANT_CLASSES += (Const,) except ImportError: # pragma: no cover # astroid is not available pass def is_constant(node): # type: (AstNode) -> bool """Returns whether node is a Constant node.""" return isinstance(node, CONSTANT_CLASSES) def is_ellipsis(node): # type: (AstNode) -> bool """Returns whether node is an Ellipsis node.""" return is_constant(node) and node.value is Ellipsis # type: ignore def is_starred(node): # type: (AstNode) -> bool """Returns whether node is a starred expression node.""" return node.__class__.__name__ == 'Starred' def is_slice(node): # type: (AstNode) -> bool """Returns whether node represents a slice, e.g. `1:2` in `x[1:2]`""" # Before 3.9, a tuple containing a slice is an ExtSlice, # but this was removed in https://bugs.python.org/issue34822 return ( node.__class__.__name__ in ('Slice', 'ExtSlice') or ( node.__class__.__name__ == 'Tuple' and any(map(is_slice, cast(ast.Tuple, node).elts)) ) ) def is_empty_astroid_slice(node): # type: (AstNode) -> bool return ( node.__class__.__name__ == "Slice" and not isinstance(node, ast.AST) and node.lower is node.upper is node.step is None ) # Sentinel value used by visit_tree(). _PREVISIT = object() def visit_tree(node, previsit, postvisit): # type: (Module, Callable[[AstNode, Optional[Token]], Tuple[Optional[Token], Optional[Token]]], Optional[Callable[[AstNode, Optional[Token], Optional[Token]], None]]) -> None """ Scans the tree under the node depth-first using an explicit stack. It avoids implicit recursion via the function call stack to avoid hitting 'maximum recursion depth exceeded' error. It calls ``previsit()`` and ``postvisit()`` as follows: * ``previsit(node, par_value)`` - should return ``(par_value, value)`` ``par_value`` is as returned from ``previsit()`` of the parent. * ``postvisit(node, par_value, value)`` - should return ``value`` ``par_value`` is as returned from ``previsit()`` of the parent, and ``value`` is as returned from ``previsit()`` of this node itself. The return ``value`` is ignored except the one for the root node, which is returned from the overall ``visit_tree()`` call. For the initial node, ``par_value`` is None. ``postvisit`` may be None. """ if not postvisit: postvisit = lambda node, pvalue, value: None iter_children = iter_children_func(node) done = set() ret = None stack = [(node, None, _PREVISIT)] # type: List[Tuple[AstNode, Optional[Token], Union[Optional[Token], object]]] while stack: current, par_value, value = stack.pop() if value is _PREVISIT: assert current not in done # protect againt infinite loop in case of a bad tree. done.add(current) pvalue, post_value = previsit(current, par_value) stack.append((current, par_value, post_value)) # Insert all children in reverse order (so that first child ends up on top of the stack). ins = len(stack) for n in iter_children(current): stack.insert(ins, (n, pvalue, _PREVISIT)) else: ret = postvisit(current, par_value, cast(Optional[Token], value)) return ret def walk(node, include_joined_str=False): # type: (AST, bool) -> Iterator[Union[Module, AstNode]] """ Recursively yield all descendant nodes in the tree starting at ``node`` (including ``node`` itself), using depth-first pre-order traversal (yieling parents before their children). This is similar to ``ast.walk()``, but with a different order, and it works for both ``ast`` and ``astroid`` trees. Also, as ``iter_children()``, it skips singleton nodes generated by ``ast``. By default, ``JoinedStr`` (f-string) nodes and their contents are skipped because they previously couldn't be handled. Set ``include_joined_str`` to True to include them. """ iter_children = iter_children_func(node) done = set() stack = [node] while stack: current = stack.pop() assert current not in done # protect againt infinite loop in case of a bad tree. done.add(current) yield current # Insert all children in reverse order (so that first child ends up on top of the stack). # This is faster than building a list and reversing it. ins = len(stack) for c in iter_children(current, include_joined_str): stack.insert(ins, c) def replace(text, replacements): # type: (str, List[Tuple[int, int, str]]) -> str """ Replaces multiple slices of text with new values. This is a convenience method for making code modifications of ranges e.g. as identified by ``ASTTokens.get_text_range(node)``. Replacements is an iterable of ``(start, end, new_text)`` tuples. For example, ``replace("this is a test", [(0, 4, "X"), (8, 9, "THE")])`` produces ``"X is THE test"``. """ p = 0 parts = [] for (start, end, new_text) in sorted(replacements): parts.append(text[p:start]) parts.append(new_text) p = end parts.append(text[p:]) return ''.join(parts) class NodeMethods: """ Helper to get `visit_{node_type}` methods given a node's class and cache the results. """ def __init__(self): # type: () -> None self._cache = {} # type: Dict[Union[ABCMeta, type], Callable[[AstNode, Token, Token], Tuple[Token, Token]]] def get(self, obj, cls): # type: (Any, Union[ABCMeta, type]) -> Callable """ Using the lowercase name of the class as node_type, returns `obj.visit_{node_type}`, or `obj.visit_default` if the type-specific method is not found. """ method = self._cache.get(cls) if not method: name = "visit_" + cls.__name__.lower() method = getattr(obj, name, obj.visit_default) self._cache[cls] = method return method def patched_generate_tokens(original_tokens): # type: (Iterable[TokenInfo]) -> Iterator[TokenInfo] """ Fixes tokens yielded by `tokenize.generate_tokens` to handle more non-ASCII characters in identifiers. Workaround for https://github.com/python/cpython/issues/68382. Should only be used when tokenizing a string that is known to be valid syntax, because it assumes that error tokens are not actually errors. Combines groups of consecutive NAME, NUMBER, and/or ERRORTOKEN tokens into a single NAME token. """ group = [] # type: List[tokenize.TokenInfo] for tok in original_tokens: if ( tok.type in (tokenize.NAME, tokenize.ERRORTOKEN, tokenize.NUMBER) # Only combine tokens if they have no whitespace in between and (not group or group[-1].end == tok.start) ): group.append(tok) else: for combined_token in combine_tokens(group): yield combined_token group = [] yield tok for combined_token in combine_tokens(group): yield combined_token def combine_tokens(group): # type: (List[tokenize.TokenInfo]) -> List[tokenize.TokenInfo] if not any(tok.type == tokenize.ERRORTOKEN for tok in group) or len({tok.line for tok in group}) != 1: return group return [ tokenize.TokenInfo( type=tokenize.NAME, string="".join(t.string for t in group), start=group[0].start, end=group[-1].end, line=group[0].line, ) ] def last_stmt(node): # type: (ast.AST) -> ast.AST """ If the given AST node contains multiple statements, return the last one. Otherwise, just return the node. """ child_stmts = [ child for child in iter_children_func(node)(node) if is_stmt(child) or type(child).__name__ in ( "excepthandler", "ExceptHandler", "match_case", "MatchCase", "TryExcept", "TryFinally", ) ] if child_stmts: return last_stmt(child_stmts[-1]) return node @lru_cache(maxsize=None) def fstring_positions_work(): # type: () -> bool """ The positions attached to nodes inside f-string FormattedValues have some bugs that were fixed in Python 3.9.7 in https://github.com/python/cpython/pull/27729. This checks for those bugs more concretely without relying on the Python version. Specifically this checks: - Values with a format spec or conversion - Repeated (i.e. identical-looking) expressions - f-strings implicitly concatenated over multiple lines. - Multiline, triple-quoted f-strings. """ source = """( f"a {b}{b} c {d!r} e {f:g} h {i:{j}} k {l:{m:n}}" f"a {b}{b} c {d!r} e {f:g} h {i:{j}} k {l:{m:n}}" f"{x + y + z} {x} {y} {z} {z} {z!a} {z:z}" f''' {s} {t} {u} {v} ''' )""" tree = ast.parse(source) name_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Name)] name_positions = [(node.lineno, node.col_offset) for node in name_nodes] positions_are_unique = len(set(name_positions)) == len(name_positions) correct_source_segments = all( ast.get_source_segment(source, node) == node.id for node in name_nodes ) return positions_are_unique and correct_source_segments def annotate_fstring_nodes(tree): # type: (ast.AST) -> None """ Add a special attribute `_broken_positions` to nodes inside f-strings if the lineno/col_offset cannot be trusted. """ if sys.version_info >= (3, 12): # f-strings were weirdly implemented until https://peps.python.org/pep-0701/ # In Python 3.12, inner nodes have sensible positions. return for joinedstr in walk(tree, include_joined_str=True): if not isinstance(joinedstr, ast.JoinedStr): continue for part in joinedstr.values: # The ast positions of the FormattedValues/Constant nodes span the full f-string, which is weird. setattr(part, '_broken_positions', True) # use setattr for mypy if isinstance(part, ast.FormattedValue): if not fstring_positions_work(): for child in walk(part.value): setattr(child, '_broken_positions', True) if part.format_spec: # this is another JoinedStr # Again, the standard positions span the full f-string. setattr(part.format_spec, '_broken_positions', True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939918.0 asttokens-3.0.0/asttokens/version.py0000644000076700000240000000002614722510216016621 0ustar00develstaff__version__ = "3.0.0" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4871798 asttokens-3.0.0/asttokens.egg-info/0000755000076700000240000000000014722510216016256 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939918.0 asttokens-3.0.0/asttokens.egg-info/PKG-INFO0000644000076700000240000001116614722510216017360 0ustar00develstaffMetadata-Version: 2.1 Name: asttokens Version: 3.0.0 Summary: Annotate AST trees with source code positions Home-page: https://github.com/gristlabs/asttokens Author: Dmitry Sagalovskiy, Grist Labs Author-email: dmitry@getgrist.com License: Apache 2.0 Keywords: code,ast,parse,tokenize,refactor Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Code Generators Classifier: Topic :: Software Development :: Compilers Classifier: Topic :: Software Development :: Interpreters Classifier: Topic :: Software Development :: Pre-processors Classifier: Environment :: Console Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Requires-Python: >=3.8 License-File: LICENSE Provides-Extra: astroid Requires-Dist: astroid<4,>=2; extra == "astroid" Provides-Extra: test Requires-Dist: astroid<4,>=2; extra == "test" Requires-Dist: pytest; extra == "test" Requires-Dist: pytest-cov; extra == "test" Requires-Dist: pytest-xdist; extra == "test" ASTTokens ========= .. image:: https://img.shields.io/pypi/v/asttokens.svg :target: https://pypi.python.org/pypi/asttokens/ .. image:: https://img.shields.io/pypi/pyversions/asttokens.svg :target: https://pypi.python.org/pypi/asttokens/ .. image:: https://github.com/gristlabs/asttokens/actions/workflows/build-and-test.yml/badge.svg :target: https://github.com/gristlabs/asttokens/actions/workflows/build-and-test.yml .. image:: https://readthedocs.org/projects/asttokens/badge/?version=latest :target: http://asttokens.readthedocs.io/en/latest/index.html .. image:: https://coveralls.io/repos/github/gristlabs/asttokens/badge.svg :target: https://coveralls.io/github/gristlabs/asttokens .. Start of user-guide The ``asttokens`` module annotates Python abstract syntax trees (ASTs) with the positions of tokens and text in the source code that generated them. It makes it possible for tools that work with logical AST nodes to find the particular text that resulted in those nodes, for example for automated refactoring or highlighting. Installation ------------ asttokens is available on PyPI: https://pypi.python.org/pypi/asttokens/:: pip install asttokens The code is on GitHub: https://github.com/gristlabs/asttokens. The API Reference is here: http://asttokens.readthedocs.io/en/latest/api-index.html. Usage ----- ASTTokens can annotate both trees built by `ast `_, AND those built by `astroid `_. Here's an example: .. code-block:: python import asttokens, ast source = "Robot('blue').walk(steps=10*n)" atok = asttokens.ASTTokens(source, parse=True) Once the tree has been marked, nodes get ``.first_token``, ``.last_token`` attributes, and the ``ASTTokens`` object offers helpful methods: .. code-block:: python attr_node = next(n for n in ast.walk(atok.tree) if isinstance(n, ast.Attribute)) print(atok.get_text(attr_node)) start, end = attr_node.last_token.startpos, attr_node.last_token.endpos print(atok.text[:start] + 'RUN' + atok.text[end:]) Which produces this output: .. code-block:: text Robot('blue').walk Robot('blue').RUN(steps=10*n) The ``ASTTokens`` object also offers methods to walk and search the list of tokens that make up the code (or a particular AST node), which is more useful and powerful than dealing with the text directly. Contribute ---------- To contribute: 1. Fork this repository, and clone your fork. 2. Install the package with test dependencies (ideally in a virtualenv) with:: pip install -e '.[test]' 3. Run tests in your current interpreter with the command ``pytest`` or ``python -m pytest``. 4. Run tests across all supported interpreters with the ``tox`` command. You will need to have the interpreters installed separately. We recommend ``pyenv`` for that. Use ``tox -p auto`` to run the tests in parallel. 5. By default certain tests which take a very long time to run are skipped, but they are run in CI. These are marked using the ``pytest`` marker ``slow`` and can be run on their own with ``pytest -m slow`` or as part of the full suite with ``pytest -m ''``. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939918.0 asttokens-3.0.0/asttokens.egg-info/SOURCES.txt0000644000076700000240000000424614722510216020150 0ustar00develstaff.coveragerc .editorconfig .gitignore .pylintrc .readthedocs.yaml LICENSE MANIFEST.in Makefile README.rst pyproject.toml setup.cfg setup.py tox.ini .github/workflows/build-and-test.yml asttokens/__init__.py asttokens/astroid_compat.py asttokens/asttokens.py asttokens/line_numbers.py asttokens/mark_tokens.py asttokens/py.typed asttokens/util.py asttokens/version.py asttokens.egg-info/PKG-INFO asttokens.egg-info/SOURCES.txt asttokens.egg-info/dependency_links.txt asttokens.egg-info/requires.txt asttokens.egg-info/top_level.txt docs/Makefile docs/api-index.rst docs/conf.py docs/index.rst docs/requirements.in docs/requirements.txt docs/user-guide.rst tests/__init__.py tests/context.py tests/test_astroid.py tests/test_asttokens.py tests/test_line_numbers.py tests/test_mark_tokens.py tests/test_tokenless.py tests/test_util.py tests/tools.py tests/testdata/README.md tests/testdata/python2/astroid/__init__.py tests/testdata/python2/astroid/absimport.py tests/testdata/python2/astroid/all.py tests/testdata/python2/astroid/clientmodule_test.py tests/testdata/python2/astroid/descriptor_crash.py tests/testdata/python2/astroid/email.py tests/testdata/python2/astroid/format.py tests/testdata/python2/astroid/joined_strings.py tests/testdata/python2/astroid/module.py tests/testdata/python2/astroid/module2.py tests/testdata/python2/astroid/noendingnewline.py tests/testdata/python2/astroid/nonregr.py tests/testdata/python2/astroid/notall.py tests/testdata/python2/astroid/recursion.py tests/testdata/python2/astroid/suppliermodule_test.py tests/testdata/python3/astroid/__init__.py tests/testdata/python3/astroid/absimport.py tests/testdata/python3/astroid/all.py tests/testdata/python3/astroid/clientmodule_test.py tests/testdata/python3/astroid/descriptor_crash.py tests/testdata/python3/astroid/email.py tests/testdata/python3/astroid/format.py tests/testdata/python3/astroid/joined_strings.py tests/testdata/python3/astroid/module.py tests/testdata/python3/astroid/module2.py tests/testdata/python3/astroid/noendingnewline.py tests/testdata/python3/astroid/nonregr.py tests/testdata/python3/astroid/notall.py tests/testdata/python3/astroid/recursion.py tests/testdata/python3/astroid/suppliermodule_test.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939918.0 asttokens-3.0.0/asttokens.egg-info/dependency_links.txt0000644000076700000240000000000114722510216022324 0ustar00develstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939918.0 asttokens-3.0.0/asttokens.egg-info/requires.txt0000644000076700000240000000011614722510216020654 0ustar00develstaff [astroid] astroid<4,>=2 [test] astroid<4,>=2 pytest pytest-cov pytest-xdist ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732939918.0 asttokens-3.0.0/asttokens.egg-info/top_level.txt0000644000076700000240000000001214722510216021001 0ustar00develstaffasttokens ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1732939918.473284 asttokens-3.0.0/docs/0000755000076700000240000000000014722510216013501 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481392848.0 asttokens-3.0.0/docs/Makefile0000644000076700000240000000113613023041320015126 0ustar00develstaff# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = asttokens 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)././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693452870.0 asttokens-3.0.0/docs/api-index.rst0000644000076700000240000000061014474005106016107 0ustar00develstaffAPI Reference ===================================== .. automodule:: asttokens ASTTokens --------- .. autoclass:: asttokens.ASTTokens :members: ASTText --------- .. autoclass:: asttokens.ASTText :members: LineNumbers ----------- .. autoclass:: asttokens.LineNumbers :members: util ---- .. automodule:: asttokens.util :members: Token, token_repr, visit_tree, walk, replace ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693452870.0 asttokens-3.0.0/docs/conf.py0000644000076700000240000000173514474005106015007 0ustar00develstaff# 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 = 'asttokens' copyright = '2023, Grist Labs' author = 'Grist Labs' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx_rtd_theme', ] templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinx_rtd_theme" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693452870.0 asttokens-3.0.0/docs/index.rst0000644000076700000240000000133314474005106015343 0ustar00develstaff.. asttokens documentation master file, created by sphinx-quickstart on Mon Aug 7 11:16:41 2023. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. ASTTokens Documentation ======================= The ``asttokens`` module annotates Python abstract syntax trees (ASTs) with the positions of tokens and text in the source code that generated them. This is helpful for tools that make source code transformations. .. toctree:: :maxdepth: 2 user-guide api-index License ------- Copyright 2023, Grist Labs, Inc. Licensed under the Apache License, Version 2.0. Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/docs/requirements.in0000644000076700000240000000037114722467250016566 0ustar00develstaff# After updating, or to pick up newer versions, run: # env/bin/pip-compile -o docs/requirements.txt docs/requirements.in # To bring venv up-to-date, run: # env/bin/pip-sync docs/requirements.txt sphinx sphinx_rtd_theme readthedocs-sphinx-search ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/docs/requirements.txt0000644000076700000240000000236614722467250017005 0ustar00develstaff# # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --config=pyproject.toml --output-file=docs/requirements.txt docs/requirements.in # alabaster==0.7.13 # via sphinx babel==2.12.1 # via sphinx certifi==2024.7.4 # via requests charset-normalizer==3.2.0 # via requests docutils==0.18.1 # via # sphinx # sphinx-rtd-theme idna==3.7 # via requests imagesize==1.4.1 # via sphinx jinja2==3.1.4 # via sphinx markupsafe==2.1.3 # via jinja2 packaging==23.1 # via sphinx pygments==2.16.1 # via sphinx readthedocs-sphinx-search==0.3.2 # via -r requirements.in requests==2.32.2 # via sphinx snowballstemmer==2.2.0 # via sphinx sphinx==6.2.1 # via # -r requirements.in # sphinx-rtd-theme # sphinxcontrib-jquery sphinx-rtd-theme==1.2.2 # via -r requirements.in sphinxcontrib-applehelp==1.0.4 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx sphinxcontrib-htmlhelp==2.0.1 # via sphinx sphinxcontrib-jquery==4.1 # via sphinx-rtd-theme sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx urllib3==2.2.2 # via requests ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481522103.0 asttokens-3.0.0/docs/user-guide.rst0000644000076700000240000000012613023435667016314 0ustar00develstaffUser Guide ========== .. include:: ../README.rst :start-after: Start of user-guide ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1681346157.0 asttokens-3.0.0/pyproject.toml0000644000076700000240000000120414415647155015476 0ustar00develstaff[build-system] requires = ["setuptools>=44", "wheel", "setuptools_scm[toml]>=3.4.3"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] write_to = "asttokens/version.py" write_to_template = "__version__ = \"{version}\"\n" [tool.mypy] show_error_codes=true warn_unused_ignores=true disallow_untyped_defs=true disallow_untyped_calls=true no_implicit_reexport=true [[tool.mypy.overrides]] module = "tests.*" disallow_untyped_defs=false disallow_untyped_calls=false ignore_missing_imports=true [[tool.mypy.overrides]] module = ["astroid", "astroid.node_classes", "astroid.nodes", "astroid.nodes.utils"] ignore_missing_imports = true ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4891605 asttokens-3.0.0/setup.cfg0000644000076700000240000000304614722510216014375 0ustar00develstaff[metadata] license_files = LICENSE name = asttokens author = Dmitry Sagalovskiy, Grist Labs author_email = dmitry@getgrist.com license = Apache 2.0 description = Annotate AST trees with source code positions keywords = code, ast, parse, tokenize, refactor url = https://github.com/gristlabs/asttokens long_description = file: README.rst classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers Topic :: Software Development :: Libraries :: Python Modules Topic :: Software Development :: Code Generators Topic :: Software Development :: Compilers Topic :: Software Development :: Interpreters Topic :: Software Development :: Pre-processors Environment :: Console Operating System :: OS Independent Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy [options] packages = asttokens setup_requires = setuptools>=44; setuptools_scm[toml]>=3.4.3 python_requires = >=3.8 [options.extras_require] astroid = astroid >=2, <4 test = astroid >=2, <4 pytest pytest-cov pytest-xdist [options.package_data] asttokens = py.typed [tool:pytest] addopts = --disable-warnings --ignore=tests/testdata --strict-markers -m 'not slow' markers = slow: marks tests as slow (deselected by default) [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1582398630.0 asttokens-3.0.0/setup.py0000644000076700000240000000032313624276246014275 0ustar00develstaff"""A setuptools based setup module. See: https://packaging.python.org/en/latest/distributing.html https://github.com/pypa/sampleproject """ from setuptools import setup if __name__ == "__main__": setup() ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1732939918.47721 asttokens-3.0.0/tests/0000755000076700000240000000000014722510216013713 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/__init__.py0000644000076700000240000000000013022720573016013 0ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1663704830.0 asttokens-3.0.0/tests/context.py0000644000076700000240000000022314312417376015756 0ustar00develstaffimport sys from os.path import abspath, dirname sys.path.insert(0, dirname(dirname(abspath(__file__)))) import asttokens __all__ = ["asttokens"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/tests/test_astroid.py0000644000076700000240000000236114722467250017004 0ustar00develstaff# -*- coding: utf-8 -*- import astroid from asttokens import ASTTokens from asttokens.astroid_compat import astroid_node_classes from . import test_mark_tokens class TestAstroid(test_mark_tokens.TestMarkTokens): is_astroid_test = True astroid_version = int(astroid.__version__.split('.')[0]) module = astroid nodes_classes = astroid_node_classes.NodeNG context_classes = [ (astroid.Name, astroid.DelName, astroid.AssignName), (astroid.Attribute, astroid.DelAttr, astroid.AssignAttr), ] @staticmethod def iter_fields(node): """ Yield a tuple of ``(fieldname, value)`` for each field that is present on *node*. Similar to ast.iter_fields, but for astroid and ignores context """ fields = node._astroid_fields + node._other_fields for field in fields: if field == 'ctx': continue if field == 'doc' and 'doc_node' in fields: continue yield field, getattr(node, field) @staticmethod def create_asttokens(source): builder = astroid.builder.AstroidBuilder() try: tree = builder.string_build(source) except AttributeError as e: raise AstroidTreeException(str(e)) return ASTTokens(source, tree=tree) class AstroidTreeException(Exception): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/tests/test_asttokens.py0000644000076700000240000001621114722467250017351 0ustar00develstaff# -*- coding: utf-8 -*- import ast import token import tokenize import unittest from .context import asttokens class TestASTTokens(unittest.TestCase): def assertTokenizing(self, generate_tokens): source = "import re # comment\n\nfoo = 'bar'\n" atok = asttokens.ASTTokens(source, tokens=generate_tokens(source)) self.assertEqual(atok.text, source) self.assertEqual([str(t) for t in atok.tokens], [ "NAME:'import'", "NAME:'re'", "COMMENT:'# comment'", "NEWLINE:'\\n'", "NL:'\\n'", "NAME:'foo'", "OP:'='", 'STRING:"\'bar\'"', "NEWLINE:'\\n'", "ENDMARKER:''" ]) self.assertEqual(atok.tokens[5].type, token.NAME) self.assertEqual(atok.tokens[5].string, 'foo') self.assertEqual(atok.tokens[5].index, 5) self.assertEqual(atok.tokens[5].startpos, 22) self.assertEqual(atok.tokens[5].endpos, 25) def test_tokenizing(self): # Test that we produce meaningful tokens on initialization. self.assertTokenizing(generate_tokens=lambda x: None) def test_given_existing_tokens(self): # type: () -> None # Test that we process a give list of tokens on initialization. self.was_called = False def generate_tokens(source): def tokens_iter(): # force nonlocal into scope for token in asttokens.util.generate_tokens(source): yield token self.was_called = True return tokens_iter() self.assertTokenizing(generate_tokens) self.assertTrue(self.was_called, "Should have used tokens from given iterable") def test_token_methods(self): # Test the methods that deal with tokens: prev/next_token, get_token, get_token_from_offset. source = "import re # comment\n\nfoo = 'bar'\n" atok = asttokens.ASTTokens(source) self.assertEqual(str(atok.tokens[3]), "NEWLINE:'\\n'") self.assertEqual(str(atok.tokens[4]), "NL:'\\n'") self.assertEqual(str(atok.tokens[5]), "NAME:'foo'") self.assertEqual(str(atok.tokens[6]), "OP:'='") self.assertEqual(atok.prev_token(atok.tokens[5]), atok.tokens[3]) self.assertEqual(atok.prev_token(atok.tokens[5], include_extra=True), atok.tokens[4]) self.assertEqual(atok.next_token(atok.tokens[5]), atok.tokens[6]) self.assertEqual(atok.next_token(atok.tokens[1]), atok.tokens[3]) self.assertEqual(atok.next_token(atok.tokens[1], include_extra=True), atok.tokens[2]) self.assertEqual(atok.get_token_from_offset(21), atok.tokens[4]) self.assertEqual(atok.get_token_from_offset(22), atok.tokens[5]) self.assertEqual(atok.get_token_from_offset(23), atok.tokens[5]) self.assertEqual(atok.get_token_from_offset(24), atok.tokens[5]) self.assertEqual(atok.get_token_from_offset(25), atok.tokens[5]) self.assertEqual(atok.get_token_from_offset(26), atok.tokens[6]) self.assertEqual(atok.get_token(2, 0), atok.tokens[4]) self.assertEqual(atok.get_token(3, 0), atok.tokens[5]) self.assertEqual(atok.get_token(3, 1), atok.tokens[5]) self.assertEqual(atok.get_token(3, 2), atok.tokens[5]) self.assertEqual(atok.get_token(3, 3), atok.tokens[5]) self.assertEqual(atok.get_token(3, 4), atok.tokens[6]) self.assertEqual(list(atok.token_range(atok.tokens[4], atok.tokens[6], include_extra=True)), atok.tokens[4:7]) # Verify that find_token works, including for non-coding tokens. self.assertEqual(atok.find_token(atok.tokens[3], token.NAME, 'foo'), atok.tokens[5]) self.assertEqual(atok.find_token(atok.tokens[3], token.NAME, 'foo', reverse=True), atok.tokens[9]) self.assertEqual(atok.find_token(atok.tokens[3], token.NAME, reverse=True), atok.tokens[1]) self.assertEqual(atok.find_token(atok.tokens[5], tokenize.COMMENT), atok.tokens[9]) self.assertEqual(atok.find_token(atok.tokens[5], tokenize.COMMENT, reverse=True), atok.tokens[2]) self.assertEqual(atok.find_token(atok.tokens[5], token.NEWLINE), atok.tokens[8]) self.assertFalse(token.ISEOF(atok.find_token(atok.tokens[5], tokenize.NEWLINE).type)) self.assertEqual(atok.find_token(atok.tokens[5], tokenize.NL), atok.tokens[9]) self.assertTrue(token.ISEOF(atok.find_token(atok.tokens[5], tokenize.NL).type)) def test_unicode_offsets(self): # ast modules provides utf8 offsets, while tokenize uses unicode offsets. Make sure we # translate correctly. source = "foo('фыва',a,b)\n" atok = asttokens.ASTTokens(source) self.assertEqual([str(t) for t in atok.tokens], [ "NAME:'foo'", "OP:'('", 'STRING:"%s"' % repr('фыва').lstrip('u'), "OP:','", "NAME:'a'", "OP:','", "NAME:'b'", "OP:')'", "NEWLINE:'\\n'", "ENDMARKER:''" ]) self.assertEqual(atok.tokens[2].startpos, 4) self.assertEqual(atok.tokens[2].endpos, 10) # Counting characters, not bytes self.assertEqual(atok.tokens[4].startpos, 11) self.assertEqual(atok.tokens[4].endpos, 12) self.assertEqual(atok.tokens[6].startpos, 13) self.assertEqual(atok.tokens[6].endpos, 14) root = ast.parse(source) # Verify that ast parser produces offsets as we expect. This is just to inform the # implementation. string_node = next(n for n in ast.walk(root) if isinstance(n, ast.Str)) self.assertEqual(string_node.lineno, 1) self.assertEqual(string_node.col_offset, 4) a_node = next(n for n in ast.walk(root) if isinstance(n, ast.Name) and n.id == 'a') self.assertEqual((a_node.lineno, a_node.col_offset), (1, 15)) # Counting bytes, not chars. b_node = next(n for n in ast.walk(root) if isinstance(n, ast.Name) and n.id == 'b') self.assertEqual((b_node.lineno, b_node.col_offset), (1, 17)) # Here we verify that we use correct offsets (translating utf8 to unicode offsets) when # extracting text ranges. atok.mark_tokens(root) self.assertEqual(atok.get_text(string_node), "'фыва'") self.assertEqual(atok.get_text(a_node), "a") self.assertEqual(atok.get_text(b_node), "b") def test_coding_declaration(self): """ASTTokens should be able to parse a string with a coding declaration.""" # In Python 2, a unicode string with a coding declaration is a SyntaxError, but we should be # able to parse a byte string with a coding declaration (as long as its utf-8 compatible). atok = asttokens.ASTTokens(str("# coding: ascii\n1\n"), parse=True) self.assertEqual([str(t) for t in atok.tokens], [ "COMMENT:'# coding: ascii'", "NL:'\\n'", "NUMBER:'1'", "NEWLINE:'\\n'", "ENDMARKER:''" ]) def test_filename(): filename = "myfile.py" atok = asttokens.ASTTokens("a", parse=True, filename=filename) assert filename == atok.filename def test_doesnt_have_location(): atok = asttokens.ASTTokens("a", parse=True) # Testing the documentation that says: # "Returns (0, 0) for nodes (like `Load`) that don't correspond # to any particular text." context = atok.tree.body[0].value.ctx assert isinstance(context, ast.Load) assert atok.get_text_range(context) == (0, 0) assert atok.get_text(context) == "" # This actually also applies to non-nodes assert atok.get_text_range(None) == (0, 0) assert atok.get_text(None) == "" if __name__ == "__main__": unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/tests/test_line_numbers.py0000644000076700000240000000531214722467250020020 0ustar00develstaff# -*- coding: utf-8 -*- import unittest from .context import asttokens class TestLineNumbers(unittest.TestCase): def test_line_numbers(self): ln = asttokens.LineNumbers("Hello\nworld\nThis\n\nis\n\na test.\n") self.assertEqual(ln.line_to_offset(1, 0), 0) self.assertEqual(ln.line_to_offset(1, 5), 5) self.assertEqual(ln.line_to_offset(2, 0), 6) self.assertEqual(ln.line_to_offset(2, 5), 11) self.assertEqual(ln.line_to_offset(3, 0), 12) self.assertEqual(ln.line_to_offset(4, 0), 17) self.assertEqual(ln.line_to_offset(5, 0), 18) self.assertEqual(ln.line_to_offset(6, 0), 21) self.assertEqual(ln.line_to_offset(7, 0), 22) self.assertEqual(ln.line_to_offset(7, 7), 29) self.assertEqual(ln.offset_to_line(0), (1, 0)) self.assertEqual(ln.offset_to_line(5), (1, 5)) self.assertEqual(ln.offset_to_line(6), (2, 0)) self.assertEqual(ln.offset_to_line(11), (2, 5)) self.assertEqual(ln.offset_to_line(12), (3, 0)) self.assertEqual(ln.offset_to_line(17), (4, 0)) self.assertEqual(ln.offset_to_line(18), (5, 0)) self.assertEqual(ln.offset_to_line(21), (6, 0)) self.assertEqual(ln.offset_to_line(22), (7, 0)) self.assertEqual(ln.offset_to_line(29), (7, 7)) # Test that out-of-bounds inputs still return something sensible. self.assertEqual(ln.line_to_offset(6, 19), 30) self.assertEqual(ln.line_to_offset(100, 99), 30) self.assertEqual(ln.line_to_offset(2, -1), 6) self.assertEqual(ln.line_to_offset(-1, 99), 0) self.assertEqual(ln.offset_to_line(30), (8, 0)) self.assertEqual(ln.offset_to_line(100), (8, 0)) self.assertEqual(ln.offset_to_line(-100), (1, 0)) def test_unicode(self): ln = asttokens.LineNumbers("фыва\nячсм") self.assertEqual(ln.line_to_offset(1, 0), 0) self.assertEqual(ln.line_to_offset(1, 4), 4) self.assertEqual(ln.line_to_offset(2, 0), 5) self.assertEqual(ln.line_to_offset(2, 4), 9) self.assertEqual(ln.offset_to_line(0), (1, 0)) self.assertEqual(ln.offset_to_line(4), (1, 4)) self.assertEqual(ln.offset_to_line(5), (2, 0)) self.assertEqual(ln.offset_to_line(9), (2, 4)) def test_utf8_offsets(self): ln = asttokens.LineNumbers("фыва\nф.в.") self.assertEqual(ln.from_utf8_col(1, 0), 0) self.assertEqual(ln.from_utf8_col(1, 2), 1) self.assertEqual(ln.from_utf8_col(1, 3), 1) self.assertEqual(ln.from_utf8_col(1, 6), 3) self.assertEqual(ln.from_utf8_col(1, 8), 4) self.assertEqual(ln.from_utf8_col(2, 0), 0) self.assertEqual(ln.from_utf8_col(2, 2), 1) self.assertEqual(ln.from_utf8_col(2, 3), 2) self.assertEqual(ln.from_utf8_col(2, 4), 2) self.assertEqual(ln.from_utf8_col(2, 5), 3) self.assertEqual(ln.from_utf8_col(2, 6), 4) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732933805.0 asttokens-3.0.0/tests/test_mark_tokens.py0000644000076700000240000010674414722474255017671 0ustar00develstaff# -*- coding: utf-8 -*- import ast import inspect import io import os import pytest import re import sys import textwrap import token from typing import List import unittest from time import time import astroid from asttokens import util, ASTTokens from . import tools try: from astroid.nodes.utils import Position as AstroidPosition except Exception: AstroidPosition = () class TestMarkTokens(unittest.TestCase): maxDiff = None # We use the same test cases to test both nodes produced by the built-in `ast` module, and by # the `astroid` library. The latter derives TestAstroid class from TestMarkTokens. For checks # that differ between them, .is_astroid_test allows to distinguish. is_astroid_test = False astroid_version = None # type: int | None module = ast def create_mark_checker(self, source, verify=True): atok = self.create_asttokens(source) checker = tools.MarkChecker(atok) # The last token should always be an ENDMARKER # None of the nodes should contain that token assert atok.tokens[-1].type == token.ENDMARKER if atok.text: # except for empty files for node in checker.all_nodes: assert node.last_token.type != token.ENDMARKER if verify: checker.verify_all_nodes(self) return checker @staticmethod def create_asttokens(source): return ASTTokens(source, parse=True) def print_timing(self): # Print the timing of mark_tokens(). This doesn't normally run as a unittest, but if you'd like # to see timings, e.g. while optimizing the implementation, run this to see them: # # nosetests -m print_timing -s tests.test_mark_tokens tests.test_astroid # # pylint: disable=no-self-use import timeit print("mark_tokens", sorted(timeit.repeat( setup=textwrap.dedent( ''' import ast, asttokens source = "foo(bar(1 + 2), 'hello' + ', ' + 'world')" atok = asttokens.ASTTokens(source) tree = ast.parse(source) '''), stmt='atok.mark_tokens(tree)', repeat=3, number=1000))) def test_mark_tokens_simple(self): source = tools.read_fixture('astroid', 'module.py') m = self.create_mark_checker(source) # Line 14 is: [indent 4] MY_DICT[key] = val self.assertEqual(m.view_nodes_at(14, 4), { "Name:MY_DICT", "Subscript:MY_DICT[key]", "Assign:MY_DICT[key] = val" }) # Line 35 is: [indent 12] raise XXXError() self.assertEqual(m.view_nodes_at(35, 12), {'Raise:raise XXXError()'}) self.assertEqual(m.view_nodes_at(35, 18), {'Call:XXXError()', 'Name:XXXError'}) # Line 53 is: [indent 12] autre = [a for (a, b) in MY_DICT if b] self.assertEqual(m.view_nodes_at(53, 20), {'ListComp:[a for (a, b) in MY_DICT if b]'}) self.assertEqual(m.view_nodes_at(53, 21), {'Name:a'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(53, 23), {'Comprehension:for (a, b) in MY_DICT if b'}) else: self.assertEqual(m.view_nodes_at(53, 23), {'comprehension:for (a, b) in MY_DICT if b'}) # Line 59 is: [indent 12] global_access(local, val=autre) self.assertEqual(m.view_node_types_at(59, 12), {'Name', 'Call', 'Expr'}) self.assertEqual(m.view_nodes_at(59, 26), {'Name:local'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(59, 33), {'Keyword:val=autre'}) else: self.assertEqual(m.view_nodes_at(59, 33), {'keyword:val=autre'}) self.assertEqual(m.view_nodes_at(59, 37), {'Name:autre'}) def test_mark_tokens_multiline(self): source = ( """( # line1 a, # line2 b + # line3 c + # line4 d # line5 )""") m = self.create_mark_checker(source) self.assertIn('Name:a', m.view_nodes_at(2, 0)) self.assertEqual(m.view_nodes_at(3, 0), { 'Name:b', 'BinOp:b + # line3\n c', 'BinOp:b + # line3\n c + # line4\n d', }) all_text = {m.atok.get_text(node) for node in m.all_nodes} self.assertEqual(all_text, { source, 'a', 'b', 'c', 'd', # All other expressions preserve newlines and comments but are parenthesized. 'b + # line3\n c', 'b + # line3\n c + # line4\n d', }) self.assertIn('Tuple:' + source, m.view_nodes_at(1, 0)) def verify_fixture_file(self, path): source = tools.read_fixture(path) m = self.create_mark_checker(source, verify=False) tested_nodes = m.verify_all_nodes(self) exp_index = 1 + (3 if self.is_astroid_test else 0) if not self.is_astroid_test: # For ast on Python 3.9, slices are expressions, we handle them and test them. if issubclass(ast.Slice, ast.expr): exp_index += 1 else: # Astroid v3 has some changes from v2 if self.astroid_version == 3: exp_index += 1 exp_tested_nodes = self.expect_tested_nodes[path][exp_index] self.assertEqual(tested_nodes, exp_tested_nodes) # There is not too much need to verify these counts. The main reason is: if we find that some # change reduces the count by a lot, it's a red flag that the test is now covering fewer nodes. expect_tested_nodes = { # AST | Astroid # Py2 Py3 Py3+slice | Py2 Py3+v2 Py3+v3 'astroid/__init__.py': ( 4, 4, 4, 4, 4, 4, ), 'astroid/absimport.py': ( 4, 3, 3, 4, 3, 3, ), 'astroid/all.py': ( 21, 23, 23, 21, 23, 23, ), 'astroid/clientmodule_test.py': ( 75, 67, 67, 69, 69, 69, ), 'astroid/descriptor_crash.py': ( 30, 28, 28, 30, 30, 30, ), 'astroid/email.py': ( 3, 3, 3, 1, 1, 1, ), 'astroid/format.py': ( 64, 61, 61, 62, 62, 62, ), 'astroid/module.py': ( 185, 174, 174, 171, 171, 173, ), 'astroid/module2.py': ( 248, 253, 255, 240, 253, 253, ), 'astroid/noendingnewline.py': ( 57, 59, 59, 57, 63, 63, ), 'astroid/notall.py': ( 15, 17, 17, 15, 17, 17, ), 'astroid/recursion.py': ( 6, 6, 6, 4, 4, 4, ), 'astroid/suppliermodule_test.py': ( 20, 17, 17, 18, 18, 18, ), } # This set of methods runs verifications for the variety of syntax constructs used in the # fixture test files. # pylint: disable=multiple-statements def test_fixture1(self): self.verify_fixture_file('astroid/__init__.py') def test_fixture2(self): self.verify_fixture_file('astroid/absimport.py') def test_fixture3(self): self.verify_fixture_file('astroid/all.py') def test_fixture4(self): self.verify_fixture_file('astroid/clientmodule_test.py') def test_fixture5(self): self.verify_fixture_file('astroid/descriptor_crash.py') def test_fixture6(self): self.verify_fixture_file('astroid/email.py') def test_fixture7(self): self.verify_fixture_file('astroid/format.py') def test_fixture8(self): self.verify_fixture_file('astroid/module.py') def test_fixture9(self): self.verify_fixture_file('astroid/module2.py') def test_fixture10(self): self.verify_fixture_file('astroid/noendingnewline.py') def test_fixture11(self): self.verify_fixture_file('astroid/notall.py') def test_fixture12(self): self.verify_fixture_file('astroid/recursion.py') def test_fixture13(self): self.verify_fixture_file('astroid/suppliermodule_test.py') def test_deep_recursion(self): # This testcase has 1050 strings joined with '+', which causes naive recursions to fail with # 'maximum recursion depth exceeded' error. We actually handle it just fine, but we can't use # to_source() on it because it chokes on recursion depth. So we test individual nodes. source = tools.read_fixture('astroid/joined_strings.py') if self.is_astroid_test: if getattr(astroid, '__version__', '1') >= '2': # Astroid 2 no longer supports this; see # https://github.com/PyCQA/astroid/issues/557#issuecomment-396004274 self.skipTest('astroid-2.0 does not support this') # Astroid < 2 does support this with optimize_ast set to True astroid.MANAGER.optimize_ast = True try: m = self.create_mark_checker(source, verify=False) finally: astroid.MANAGER.optimize_ast = False self.assertEqual(len(m.all_nodes), 4) # This is the result of astroid's optimization self.assertEqual(m.view_node_types_at(1, 0), {'Module', 'Assign', 'AssignName'}) const = next(n for n in m.all_nodes if isinstance(n, astroid.nodes.Const)) # TODO: Astroid's optimization makes it impossible to get the right start-end information # for the combined node. So this test fails. To avoid it, don't set 'optimize_ast=True'. To # fix it, astroid would probably need to record the info from the nodes it's combining. Or # astroid could avoid the need for the optimization by using an explicit stack like we do. #self.assertEqual(m.atok.get_text_range(const), (5, len(source) - 1)) else: m = self.create_mark_checker(source, verify=False) self.assertEqual(len(m.all_nodes), 2104) self.assertEqual(m.view_node(m.all_nodes[-1]), "Constant:'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7'") self.assertEqual(m.view_node(m.all_nodes[-2]), "Constant:'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA'") self.assertEqual(m.view_node(m.all_nodes[1053]), "Constant:'R0lGODlhigJnAef/AAABAAEEAAkCAAMGAg0GBAYJBQoMCBMODQ4QDRITEBkS'") self.assertEqual(m.view_node(m.all_nodes[1052]), "BinOp:'R0lGODlhigJnAef/AAABAAEEAAkCAAMGAg0GBAYJBQoMCBMODQ4QDRITEBkS'\r\n" + " +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp'") binop = next(n for n in m.all_nodes if n.__class__.__name__ == 'BinOp') self.assertTrue(m.atok.get_text(binop).startswith("'R0l")) self.assertTrue(m.atok.get_text(binop).endswith("AA7'")) assign = next(n for n in m.all_nodes if n.__class__.__name__ == 'Assign') self.assertTrue(m.atok.get_text(assign).startswith("x = (")) self.assertTrue(m.atok.get_text(assign).endswith(")")) def test_slices(self): # Make sure we don't fail on parsing slices of the form `foo[4:]`. source = "(foo.Area_Code, str(foo.Phone)[:3], str(foo.Phone)[3:], foo[:], bar[::2, :], bar2[:, ::2], [a[:]][::-1])" m = self.create_mark_checker(source) self.assertIn("Tuple:" + source, m.view_nodes_at(1, 0)) self.assertEqual(m.view_nodes_at(1, 1), { "Attribute:foo.Area_Code", "Name:foo" }) self.assertEqual(m.view_nodes_at(1, 16), { "Subscript:str(foo.Phone)[:3]", "Call:str(foo.Phone)", "Name:str"}) self.assertEqual(m.view_nodes_at(1, 36), { "Subscript:str(foo.Phone)[3:]", "Call:str(foo.Phone)", "Name:str"}) # Slice and ExtSlice nodes are wrong, and in particular placed with parents. They are not very # important, so we skip them here. self.assertEqual({n for n in m.view_nodes_at(1, 56) if 'Slice:' not in n}, { "Subscript:foo[:]", "Name:foo" }) self.assertEqual({n for n in m.view_nodes_at(1, 64) if 'Slice:' not in n and 'Tuple:' not in n}, { "Subscript:bar[::2, :]", "Name:bar" }) def test_adjacent_strings(self): source = """ foo = 'x y z' \\ '''a b c''' "u v w" bar = ('x y z' # comment2 'a b c' # comment3 'u v w' ) """ m = self.create_mark_checker(source) node_name = 'Const' if self.is_astroid_test else 'Constant' self.assertEqual(m.view_nodes_at(2, 6), { node_name + ":'x y z' \\\n'''a b c''' \"u v w\"" }) self.assertEqual(m.view_nodes_at(4, 7), { node_name + ":'x y z' # comment2\n 'a b c' # comment3\n 'u v w'" }) def test_print_function(self): # This testcase imports print as function (using from __future__). Check that we can parse it. # verify_all_nodes doesn't work on Python 2 because the print() call parsed in isolation # is viewed as a Print node since it doesn't see the future import source = tools.read_fixture('astroid/nonregr.py') m = self.create_mark_checker(source, verify=True) # Line 16 is: [indent 8] print(v.get('yo')) self.assertEqual(m.view_nodes_at(16, 8), { "Call:print(v.get('yo'))", "Expr:print(v.get('yo'))", "Name:print" }) self.assertEqual(m.view_nodes_at(16, 14), {"Call:v.get('yo')", "Attribute:v.get", "Name:v"}) # To make sure we can handle various hard cases, we include tests for issues reported for a # similar project here: https://bitbucket.org/plas/thonny def test_nonascii(self): # Test of https://bitbucket.org/plas/thonny/issues/162/weird-range-marker-crash-with-non-ascii # Only on PY3 because Py2 doesn't support unicode identifiers. for source in ( "℘·2=1+a℘·b+a℘·2b", # example from https://github.com/python/cpython/issues/68382 "sünnikuupäev=str((18+int(isikukood[0:1])-1)//2)+isikukood[1:3]", "sünnikuupaev=str((18+int(isikukood[0:1])-1)//2)+isikukood[1:3]"): m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(1, 0), { "Module:%s" % source, "Assign:%s" % source, "%s:%s" % ("AssignName" if self.is_astroid_test else "Name", source.split("=")[0]), }) def test_bytes_smoke(self): const = 'Const' if self.is_astroid_test else ( 'Constant' if sys.version_info >= (3, 8) else 'Bytes' ) for source in ( 'b"123abd"', r"b'\x12\x3a\xbc'", ): expected = { "Module:" + source, const + ":" + source, "Expr:" + source, } m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(1, 1), expected) m.verify_all_nodes(self) if sys.version_info[0:2] >= (3, 6): # f-strings are only supported in Python36. We don't handle them fully, for a couple of # reasons: parsed AST nodes are not annotated with correct line and col_offset (see # https://bugs.python.org/issue29051), and there are confusingly two levels of tokenizing. # Meanwhile, we only parse to the level of JoinedStr, and verify that. def test_fstrings(self): for source in ( '(f"He said his name is {name!r}.",)', "f'{function(kwarg=24)}'", '''(f'{f"""{f"{val!r}"}"""}')''', 'a = f"""result: {value:{width}.{precision}}"""', """[f"abc {a['x']} def"]""", "def t():\n return f'{function(kwarg=24)}'"): self.create_mark_checker(source) def test_adjacent_joined_strings(self): source = """ foo = f'x y z' \\ f'''a b c''' f"u v w" bar = ('x y z' # comment2 'a b c' # comment3 f'u v w' ) """ m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(2, 6), { "JoinedStr:f'x y z' \\\nf'''a b c''' f\"u v w\"" }) self.assertEqual(m.view_nodes_at(4, 7), { "JoinedStr:'x y z' # comment2\n 'a b c' # comment3\n f'u v w'" }) if sys.version_info >= (3, 12): def test_fstrings_3_12_plus(self): const = 'Const' if self.is_astroid_test else 'Constant' m = self.create_mark_checker( 'x = (f"Wobble {f"{func(kwarg=f"{boo!r}")}"!r}.",)', ) self.assertEqual(m.view_nodes_at(1, 6), { 'JoinedStr:f"Wobble {f"{func(kwarg=f"{boo!r}")}"!r}."', }) # TODO: Nodes within an f-string currently don't have tokens attached so # we don't get their text ranges (hence no text after the colon in the # below assertion). Ideally we should update the mark-tokens logic to # attach tokens to the nodes within f-strings, at which point this test # should be updated with the relevant node texts. node, = m.get_nodes_at(1, 6) self.assertEqual( [ const + ':', 'FormattedValue:', const + ':', ], [m.view_node(x) for x in node.values], "Wrong children within JoinedStr", ) m.verify_all_nodes(self) def test_splat(self): # See https://bitbucket.org/plas/thonny/issues/151/debugger-crashes-when-encountering-a-splat source = textwrap.dedent(""" arr = [1,2,3,4,5] def print_all(a, b, c, d, e): print(a, b, c, d ,e) print_all(*arr) """) m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(5, 0), { "Expr:print_all(*arr)", "Call:print_all(*arr)", "Name:print_all" }) self.assertEqual(m.view_nodes_at(5, 10), { "Starred:*arr" }) self.assertEqual(m.view_nodes_at(5, 11), { "Name:arr" }) def test_paren_attr(self): # See https://bitbucket.org/plas/thonny/issues/123/attribute-access-on-parenthesized source = "(x).foo()" m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(1, 1), {"Name:x"}) self.assertEqual(m.view_nodes_at(1, 0), {"Module:(x).foo()", "Expr:(x).foo()", "Call:(x).foo()", "Attribute:(x).foo"}) def test_conditional_expr(self): # See https://bitbucket.org/plas/thonny/issues/108/ast-marker-crashes-with-conditional source = "a = True if True else False\nprint(a)" m = self.create_mark_checker(source) name_a = 'AssignName:a' if self.is_astroid_test else 'Name:a' const_true = ('Const:True' if self.is_astroid_test else 'Constant:True') self.assertEqual(m.view_nodes_at(1, 0), {name_a, "Assign:a = True if True else False", "Module:" + source}) self.assertEqual(m.view_nodes_at(1, 4), {const_true, 'IfExp:True if True else False'}) self.assertEqual(m.view_nodes_at(2, 0), {"Name:print", "Call:print(a)", "Expr:print(a)"}) def test_calling_lambdas(self): # See https://bitbucket.org/plas/thonny/issues/96/calling-lambdas-crash-the-debugger source = "y = (lambda x: x + 1)(2)" m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(1, 4), {'Call:(lambda x: x + 1)(2)'}) self.assertEqual(m.view_nodes_at(1, 15), {'BinOp:x + 1', 'Name:x'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(1, 0), {'AssignName:y', 'Assign:'+source, 'Module:'+source}) else: self.assertEqual(m.view_nodes_at(1, 0), {'Name:y', 'Assign:' + source, 'Module:' + source}) def test_comprehensions(self): # See https://bitbucket.org/plas/thonny/issues/8/range-marker-doesnt-work-correctly-with for source in ( "[(key, val) for key, val in ast.iter_fields(node)]", "((key, val) for key, val in ast.iter_fields(node))", "{(key, val) for key, val in ast.iter_fields(node)}", "{key: val for key, val in ast.iter_fields(node)}", "[[c for c in key] for key, val in ast.iter_fields(node)]"): self.create_mark_checker(source) def test_trailing_commas(self): # Make sure we handle trailing commas on comma-separated structures (e.g. tuples, sets, etc.) for source in ( "(a,b,)", "[c,d,]", "{e,f,}", "{h:1,i:2,}"): self.create_mark_checker(source) def test_tuples(self): def get_tuples(code): m = self.create_mark_checker(code) return [m.atok.get_text(n) for n in m.all_nodes if n.__class__.__name__ == "Tuple"] self.assertEqual(get_tuples("a,"), ["a,"]) self.assertEqual(get_tuples("(a,)"), ["(a,)"]) self.assertEqual(get_tuples("(a),"), ["(a),"]) self.assertEqual(get_tuples("((a),)"), ["((a),)"]) self.assertEqual(get_tuples("(a,),"), ["(a,),", "(a,)"]) self.assertEqual(get_tuples("((a,),)"), ["((a,),)", "(a,)"]) self.assertEqual(get_tuples("()"), ["()"]) self.assertEqual(get_tuples("(),"), ["(),", "()"]) self.assertEqual(get_tuples("((),)"), ["((),)", "()"]) self.assertEqual(get_tuples("((),(a,))"), ["((),(a,))", "()", "(a,)"]) self.assertEqual(get_tuples("((),(a,),)"), ["((),(a,),)", "()", "(a,)"]) self.assertEqual(get_tuples("((),(a,),),"), ["((),(a,),),", "((),(a,),)", "()", "(a,)"]) self.assertEqual(get_tuples('((foo, bar),)'), ['((foo, bar),)', '(foo, bar)']) self.assertEqual(get_tuples('(foo, bar),'), ['(foo, bar),', '(foo, bar)']) self.assertEqual(get_tuples('def foo(a=()): ((x, (y,)),) = ((), (a,),),'), [ '()', '((x, (y,)),)', '(x, (y,))', '(y,)', '((), (a,),),', '((), (a,),)', '()', '(a,)']) self.assertEqual(get_tuples('def foo(a=()): ((x, (y,)),) = [(), [a,],],'), [ '()', '((x, (y,)),)', '(x, (y,))', '(y,)', '[(), [a,],],', '()']) def test_dict_order(self): # Make sure we iterate over dict keys/values in source order. # See https://github.com/gristlabs/asttokens/issues/31 source = 'f({1: (2), 3: 4}, object())' self.create_mark_checker(source) def test_del_dict(self): # See https://bitbucket.org/plas/thonny/issues/24/try-del-from-dictionary-in-debugging-mode source = "x = {4:5}\ndel x[4]" m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(1, 4), {'Dict:{4:5}'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(1, 5), {'Const:4'}) else: self.assertEqual(m.view_nodes_at(1, 5), {'Constant:4'}) self.assertEqual(m.view_nodes_at(2, 0), {'Delete:del x[4]'}) self.assertEqual(m.view_nodes_at(2, 4), {'Name:x', 'Subscript:x[4]'}) def test_bad_tokenless_types(self): # Cases where _get_text_positions_tokenless is incorrect in 3.8. source = textwrap.dedent(""" def foo(*, name: str): # keyword-only argument with type annotation pass f(*(x)) # ast.Starred with parentheses """) self.create_mark_checker(source) def test_return_annotation(self): # See https://bitbucket.org/plas/thonny/issues/9/range-marker-crashes-on-function-return source = textwrap.dedent(""" def liida_arvud(x: int, y: int) -> int: return x + y """) m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(2, 0), {'FunctionDef:def liida_arvud(x: int, y: int) -> int:\n return x + y'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(2, 16), {'Arguments:x: int, y: int', 'AssignName:x'}) else: self.assertEqual(m.view_nodes_at(2, 16), {'arguments:x: int, y: int', 'arg:x: int'}) self.assertEqual(m.view_nodes_at(2, 19), {'Name:int'}) self.assertEqual(m.view_nodes_at(2, 35), {'Name:int'}) self.assertEqual(m.view_nodes_at(3, 2), {'Return:return x + y'}) def test_keyword_arg_only(self): # See https://bitbucket.org/plas/thonny/issues/52/range-marker-fails-with-ridastrip-split source = "f(x=1)\ng(a=(x),b=[y])" m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(1, 0), {'Name:f', 'Call:f(x=1)', 'Expr:f(x=1)', 'Module:' + source}) self.assertEqual(m.view_nodes_at(2, 0), {'Name:g', 'Call:g(a=(x),b=[y])', 'Expr:g(a=(x),b=[y])'}) self.assertEqual(m.view_nodes_at(2, 11), {'Name:y'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(1, 2), {'Keyword:x=1'}) self.assertEqual(m.view_nodes_at(1, 4), {'Const:1'}) self.assertEqual(m.view_nodes_at(2, 2), {'Keyword:a=(x)'}) self.assertEqual(m.view_nodes_at(2, 8), {'Keyword:b=[y]'}) else: self.assertEqual(m.view_nodes_at(1, 2), {'keyword:x=1'}) self.assertEqual(m.view_nodes_at(1, 4), {'Constant:1'}) self.assertEqual(m.view_nodes_at(2, 2), {'keyword:a=(x)'}) self.assertEqual(m.view_nodes_at(2, 8), {'keyword:b=[y]'}) def test_decorators(self): # See https://bitbucket.org/plas/thonny/issues/49/range-marker-fails-with-decorators source = textwrap.dedent(""" @deco1 def f(): pass @deco2(a=1) def g(x): pass @deco3() def g(x): pass @deco4 class C: pass """) m = self.create_mark_checker(source) # The `arguments` node has bogus positions here (and whenever there are no arguments). We # don't let that break our test because it's unclear if it matters to anything anyway. self.assertIn('FunctionDef:@deco1\ndef f():\n pass', m.view_nodes_at(2, 0)) self.assertEqual(m.view_nodes_at(2, 1), {'Name:deco1'}) if self.is_astroid_test: self.assertEqual(m.view_nodes_at(5, 0), { 'FunctionDef:@deco2(a=1)\ndef g(x):\n pass', 'Decorators:@deco2(a=1)' }) else: self.assertEqual(m.view_nodes_at(5, 0), {'FunctionDef:@deco2(a=1)\ndef g(x):\n pass'}) self.assertEqual(m.view_nodes_at(5, 1), {'Name:deco2', 'Call:deco2(a=1)'}) self.assertEqual(m.view_nodes_at(9, 1), {'Name:deco3', 'Call:deco3()'}) def test_with(self): source = "with foo: pass" m = self.create_mark_checker(source) self.assertEqual(m.view_node_types_at(1, 0), {"Module", "With"}) self.assertEqual(m.view_nodes_at(1, 0), { "Module:with foo: pass", "With:with foo: pass", }) source = textwrap.dedent( ''' def f(x): with A() as a: log(a) with B() as b, C() as c: log(b, c) log(x) ''') # verification fails on Python2 which turns `with X, Y` turns into `with X: with Y`. m = self.create_mark_checker(source, verify=True) self.assertEqual(m.view_nodes_at(5, 4), { 'With:with B() as b, C() as c: log(b, c)' }) self.assertEqual(m.view_nodes_at(3, 2), { 'With: with A() as a:\n log(a)\n with B() as b, C() as c: log(b, c)' }) with_nodes = [n for n in m.all_nodes if n.__class__.__name__ == 'With'] self.assertEqual({m.view_node(n) for n in with_nodes}, { 'With:with B() as b, C() as c: log(b, c)', 'With: with A() as a:\n log(a)\n with B() as b, C() as c: log(b, c)', }) def test_one_line_if_elif(self): source = """ if 1: a elif 2: b """ self.create_mark_checker(source) def test_statements_with_semicolons(self): source = """ a; b; c( 17 ); d # comment1; comment2 if 2: a; b; # comment3 if a: if b: c; d # comment4 """ m = self.create_mark_checker(source) self.assertEqual( [m.atok.get_text(n) for n in m.all_nodes if util.is_stmt(n)], ['a', 'b', 'c(\n 17\n)', 'd', 'if 2: a; b', 'a', 'b', 'if a:\n if b: c; d', 'if b: c; d', 'c', 'd']) def test_complex_numbers(self): source = """ 1 -1 j # not a complex number, just a name 1j -1j 1+2j 3-4j 1j-1j-1j-1j """ self.create_mark_checker(source) def test_parens_around_func(self): source = textwrap.dedent( ''' foo() (foo)() (lambda: 0)() (lambda: ())() (foo)((1)) (lambda: ())((2)) x = (obj.attribute.get_callback() or default_callback)() ''') m = self.create_mark_checker(source) self.assertEqual(m.view_nodes_at(2, 0), {"Name:foo", "Expr:foo()", "Call:foo()"}) self.assertEqual(m.view_nodes_at(3, 1), {"Name:foo"}) self.assertEqual(m.view_nodes_at(3, 0), {"Expr:(foo)()", "Call:(foo)()"}) self.assertEqual(m.view_nodes_at(4, 0), {"Expr:(lambda: 0)()", "Call:(lambda: 0)()"}) self.assertEqual(m.view_nodes_at(5, 0), {"Expr:(lambda: ())()", "Call:(lambda: ())()"}) self.assertEqual(m.view_nodes_at(6, 0), {"Expr:(foo)((1))", "Call:(foo)((1))"}) self.assertEqual(m.view_nodes_at(7, 0), {"Expr:(lambda: ())((2))", "Call:(lambda: ())((2))"}) self.assertEqual(m.view_nodes_at(8, 4), {"Call:(obj.attribute.get_callback() or default_callback)()"}) self.assertIn('BoolOp:obj.attribute.get_callback() or default_callback', m.view_nodes_at(8, 5)) def test_complex_slice_and_parens(self): source = 'f((x)[:, 0])' self.create_mark_checker(source) @pytest.mark.slow def test_sys_modules(self): """ Verify all nodes on source files obtained from sys.modules. This can take a long time as there are many modules, so is marked as a slow test and must be explicitly selected for running. """ from .test_astroid import AstroidTreeException modules = list(sys.modules.values()) start = time() for module in modules: # Don't let this test (which runs twice) take longer than 13 minutes # to avoid the travis build time limit of 30 minutes if time() - start > 13 * 60: break try: filename = inspect.getsourcefile(module) except Exception: # some modules raise weird errors continue if not filename: continue filename = os.path.abspath(filename) print(filename) try: with io.open(filename) as f: source = f.read() except OSError: continue if self.is_astroid_test and ( # Astroid fails with a syntax error if a type comment is on its own line re.search(r'^\s*# type: ', source, re.MULTILINE) ): print('Skipping', filename) continue try: self.create_mark_checker(source) except AstroidTreeException: # Astroid sometimes fails with errors like: # AttributeError: 'TreeRebuilder' object has no attribute 'visit_typealias' # See https://github.com/gristlabs/asttokens/actions/runs/6015907789/job/16318767911?pr=110 # Should be fixed in the next astroid release: # https://github.com/pylint-dev/pylint/issues/8782#issuecomment-1669967220 # Note that this exception is raised before asttokens is even involved, # it's purely an astroid bug that we can safely ignore. continue def test_dict_merge(self): self.create_mark_checker("{**{}}") def test_async_def(self): self.create_mark_checker(""" async def foo(): pass @decorator async def foo(): pass """) def test_async_for_and_with(self): # Can't verify all nodes because in < 3.7 # async for/with outside of a function is invalid syntax m = self.create_mark_checker(""" async def foo(): async for x in y: pass async with x as y: pass """, verify=False) assert m.view_nodes_at(3, 2) == {"AsyncFor:async for x in y: pass"} assert m.view_nodes_at(4, 2) == {"AsyncWith:async with x as y: pass"} def test_await(self): # Can't verify all nodes because in astroid # await outside of an async function is invalid syntax m = self.create_mark_checker(""" async def foo(): await bar """, verify=False) assert m.view_nodes_at(3, 2) == {"Await:await bar", "Expr:await bar"} if sys.version_info >= (3, 8): def test_assignment_expressions(self): # From https://www.python.org/dev/peps/pep-0572/ self.create_mark_checker(""" # Handle a matched regex if (match := pattern.search(data)) is not None: # Do something with match pass # A loop that can't be trivially rewritten using 2-arg iter() while chunk := file.read(8192): process(chunk) # Reuse a value that's expensive to compute [y := f(x), y**2, y**3] # Share a subexpression between a comprehension filter clause and its output filtered_data = [y for x in data if (y := f(x)) is not None] y0 = (y1 := f(x)) # Valid, though discouraged foo(x=(y := f(x))) # Valid, though probably confusing def foo(answer=(p := 42)): # Valid, though not great style ... def foo(answer: (p := 42) = 5): # Valid, but probably never useful ... lambda: (x := 1) # Valid, but unlikely to be useful (x := lambda: 1) # Valid lambda line: (m := re.match(pattern, line)) and m.group(1) # Valid if any((comment := line).startswith('#') for line in lines): print("First comment:", comment) if all((nonblank := line).strip() == '' for line in lines): print("All lines are blank") partial_sums = [total := total + v for v in values] """) if sys.version_info >= (3, 10): def test_match_case(self): m = self.create_mark_checker(""" if 0: match x: case ast.BinOp(): if z: pass case cls(a,b) if y: pass case _: match y: case 1: pass case _: pass """) self.assertEqual(m.view_nodes_at(10, 6), { 'Match:' ' match y:\n' ' case 1:\n' ' pass\n' ' case _:\n' ' pass', }) def parse_snippet(self, text, node): """ Returns the parsed AST tree for the given text, handling issues with indentation and newlines when text is really an extracted part of larger code. """ # If text is indented, it's a statement, and we need to put in a scope for indents to be valid # (using textwrap.dedent is insufficient because some lines may not indented, e.g. comments or # multiline strings). If text is an expression but has newlines, we parenthesize it to make it # parsable. # For expressions and statements, we add a dummy statement '_' before it because if it's just a # string contained in an astroid.Const or astroid.Expr it will end up in the doc attribute and be # a pain to extract for comparison # For starred expressions, e.g. `*args`, we wrap it in a function call to make it parsable. # For slices, e.g. `x:`, we wrap it in an indexing expression to make it parsable. indented = re.match(r'^[ \t]+\S', text) if indented: return self.module.parse('def dummy():\n' + text).body[0].body[0] if util.is_starred(node): return self.module.parse('f(' + text + ')').body[0].value.args[0] if util.is_slice(node): return self.module.parse('a[' + text + ']').body[0].value.slice if util.is_expr(node): return self.module.parse('_\n(' + text + ')').body[1].value if util.is_module(node): return self.module.parse(text) return self.module.parse('_\n' + text).body[1] def test_assert_nodes_equal(self): """ Checks that assert_nodes_equal actually fails when given different nodes """ def check(s1, s2): n1 = self.module.parse(s1) n2 = self.module.parse(s2) with self.assertRaises(AssertionError): self.assert_nodes_equal(n1, n2) check('a', 'b') check('a*b', 'a+b') check('a*b', 'b*a') check('(a and b) or c', 'a and (b or c)') check('a = 1', 'a = 2') check('a = 1', 'a += 1') check('a *= 1', 'a += 1') check('[a for a in []]', '[a for a in ()]') check("for x in y: pass", "for x in y: fail") check("1", "1.0") check("foo(a, b, *d, c=2, **e)", "foo(a, b, *d, c=2.0, **e)") check("foo(a, b, *d, c=2, **e)", "foo(a, b, *d, c=2)") check('def foo():\n """xxx"""\n None', 'def foo():\n """xx"""\n None') nodes_classes = ast.AST context_classes = [ast.expr_context] # type: List[util.AstNode] iter_fields = staticmethod(ast.iter_fields) def assert_nodes_equal(self, t1, t2): # Ignore the context of each node which can change when parsing # substrings of source code. We just want equal structure and contents. for context_classes_group in self.context_classes: if isinstance(t1, context_classes_group): self.assertIsInstance(t2, context_classes_group) break else: self.assertEqual(type(t1), type(t2)) if isinstance(t1, AstroidPosition): # Ignore the lineno/col_offset etc. from astroid return if isinstance(t1, (list, tuple)): self.assertEqual(len(t1), len(t2)) for vc1, vc2 in zip(t1, t2): self.assert_nodes_equal(vc1, vc2) elif isinstance(t1, self.nodes_classes): self.assert_nodes_equal( list(self.iter_fields(t1)), list(self.iter_fields(t2)), ) else: # Weird bug in astroid that collapses spaces in docstrings sometimes maybe if self.is_astroid_test and isinstance(t1, str): t1 = re.sub(r'^ +$', '', t1, flags=re.MULTILINE) t2 = re.sub(r'^ +$', '', t2, flags=re.MULTILINE) self.assertEqual(t1, t2) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1715926931.0 asttokens-3.0.0/tests/test_tokenless.py0000644000076700000240000001033114621573623017342 0ustar00develstaffimport ast import sys import unittest from asttokens import ASTText, supports_tokenless from asttokens.util import fstring_positions_work source = """ x = 1 if x > 0: for i in range(10): print(i) else: print('negative') def foo(bar): pass print(f"{xx + 22} is negative {1.23:.2f} {'a'!r} {yy =} {aa:{bb}}") import a import b as c, d.e as f from foo.bar import baz as spam """ fstring_node_dumps = [ ast.dump(ast.parse(s).body[0].value) # type: ignore for s in ["xx", "yy", "aa", "bb", "xx + 22", "22", "1.23", "'a'"] ] def is_fstring_internal_node(node): """ Returns True if the given node is an internal node in an f-string. Only applies for nodes parsed from the source above. """ return ast.dump(node) in fstring_node_dumps def is_fstring_format_spec(node): """ Returns True if the given node is a format specifier in an f-string. Only applies for nodes parsed from the source above. """ return ( isinstance(node, ast.JoinedStr) and len(node.values) == 1 and ( ( isinstance(node.values[0], ast.Str) and node.values[0].value in ['.2f'] ) or ( isinstance(node.values[0], ast.FormattedValue) and isinstance(node.values[0].value, ast.Name) and node.values[0].value.id == 'bb' ) ) ) @unittest.skipUnless(supports_tokenless(), "Python version does not support not using tokens") class TestTokenless(unittest.TestCase): def test_get_text_tokenless(self): atok = ASTText(source) for node in ast.walk(atok.tree): if not isinstance(node, (ast.arguments, ast.arg)): self.check_node(atok, node) self.assertTrue(supports_tokenless(node), node) # Check that we didn't need to fall back to using tokens self.assertIsNone(atok._asttokens) has_tokens = False for node in ast.walk(atok.tree): self.check_node(atok, node) if isinstance(node, ast.arguments): has_tokens = True self.assertEqual(atok._asttokens is not None, has_tokens) # Now we have started using tokens as fallback self.assertIsNotNone(atok._asttokens) self.assertTrue(has_tokens) def check_node(self, atok, node): if not hasattr(node, 'lineno'): self.assertEqual(ast.get_source_segment(source, node), None) atok_text = atok.get_text(node) if not isinstance(node, (ast.arg, ast.arguments)): self.assertEqual(atok_text, source if isinstance(node, ast.Module) else '', node) return for padded in [True, False]: ast_text = ast.get_source_segment(source, node, padded=padded) atok_text = atok.get_text(node, padded=padded) if ast_text: if sys.version_info < (3, 12) and ( ast_text.startswith("f") and isinstance(node, (ast.Str, ast.FormattedValue)) or is_fstring_format_spec(node) or (not fstring_positions_work() and is_fstring_internal_node(node)) ): self.assertEqual(atok_text, "", node) else: self.assertEqual(atok_text, ast_text, node) self.assertEqual( atok.get_text_positions(node, padded=False), ( (node.lineno, node.col_offset), (node.end_lineno, node.end_col_offset), ), ) def test_nested_fstrings(self): f1 = 'f"a {1+2} b {3+4} c"' f2 = "f'd {" + f1 + "} e'" f3 = "f'''{" + f2 + "}{" + f1 + "}'''" f4 = 'f"""{' + f3 + '}"""' s = 'f = ' + f4 atok = ASTText(s) self.assertEqual(atok.get_text(atok.tree), s) n4 = atok.tree.body[0].value n3 = n4.values[0].value n2 = n3.values[0].value n1 = n2.values[1].value self.assertEqual(atok.get_text(n4), f4) if fstring_positions_work(): self.assertEqual(atok.get_text(n3), f3) self.assertEqual(atok.get_text(n2), f2) self.assertEqual(atok.get_text(n1), f1) else: self.assertEqual(atok.get_text(n3), '') self.assertEqual(atok.get_text(n2), '') self.assertEqual(atok.get_text(n1), '') class TestFstringPositionsWork(unittest.TestCase): def test_fstring_positions_work(self): self.assertEqual( fstring_positions_work() and supports_tokenless(), sys.version_info >= (3, 10, 6) and 'pypy' not in sys.version.lower(), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/tests/test_util.py0000644000076700000240000001300114722467250016305 0ustar00develstaff# -*- coding: utf-8 -*- import ast import io import sys import token import unittest import astroid import pytest from .context import asttokens from .tools import get_node_name class TestUtil(unittest.TestCase): def print_timing(self): # pylint: disable=no-self-use # Test the implementation of asttokens.util.walk, which uses the same approach as # visit_tree(). This doesn't run as a normal unittest, but if you'd like to see timings, e.g. # after experimenting with the implementation, run this to see them: # # nosetests -i print_timing -s tests.test_util # import timeit import textwrap setup = textwrap.dedent( ''' import ast, asttokens source = "foo(bar(1 + 2), 'hello' + ', ' + 'world')" atok = asttokens.ASTTokens(source, parse=True) ''') print("ast", sorted(timeit.repeat( setup=setup, number=10000, stmt='len(list(ast.walk(atok.tree)))'))) print("util", sorted(timeit.repeat( setup=setup, number=10000, stmt='len(list(asttokens.util.walk(atok.tree)))'))) source = "foo(bar(1 + 2), 'hello' + ', ' + 'world')" def test_walk_ast(self): atok = asttokens.ASTTokens(self.source, parse=True) def view(node): return "%s:%s" % (get_node_name(node), atok.get_text(node)) scan = [view(n) for n in asttokens.util.walk(atok.tree)] self.assertEqual(scan, [ "Module:foo(bar(1 + 2), 'hello' + ', ' + 'world')", "Expr:foo(bar(1 + 2), 'hello' + ', ' + 'world')", "Call:foo(bar(1 + 2), 'hello' + ', ' + 'world')", 'Name:foo', 'Call:bar(1 + 2)', 'Name:bar', 'BinOp:1 + 2', 'Constant:1', 'Constant:2', "BinOp:'hello' + ', ' + 'world'", "BinOp:'hello' + ', '", "Constant:'hello'", "Constant:', '", "Constant:'world'" ]) def test_walk_astroid(self): atok = asttokens.ASTTokens(self.source, tree=astroid.builder.parse(self.source)) def view(node): return "%s:%s" % (get_node_name(node), atok.get_text(node)) scan = [view(n) for n in asttokens.util.walk(atok.tree)] self.assertEqual(scan, [ "Module:foo(bar(1 + 2), 'hello' + ', ' + 'world')", "Expr:foo(bar(1 + 2), 'hello' + ', ' + 'world')", "Call:foo(bar(1 + 2), 'hello' + ', ' + 'world')", 'Name:foo', 'Call:bar(1 + 2)', 'Name:bar', 'BinOp:1 + 2', 'Const:1', 'Const:2', "BinOp:'hello' + ', ' + 'world'", "BinOp:'hello' + ', '", "Const:'hello'", "Const:', '", "Const:'world'" ]) def test_replace(self): self.assertEqual(asttokens.util.replace("this is a test", [(0, 4, "X"), (8, 9, "THE")]), "X is THE test") self.assertEqual(asttokens.util.replace("this is a test", []), "this is a test") self.assertEqual(asttokens.util.replace("this is a test", [(7,7," NOT")]), "this is NOT a test") source = "foo(bar(1 + 2), 'hello' + ', ' + 'world')" atok = asttokens.ASTTokens(source, parse=True) names = [n for n in asttokens.util.walk(atok.tree) if isinstance(n, ast.Name)] strings = [n for n in asttokens.util.walk(atok.tree) if isinstance(n, ast.Str)] repl1 = [atok.get_text_range(n) + ('TEST',) for n in names] repl2 = [atok.get_text_range(n) + ('val',) for n in strings] self.assertEqual(asttokens.util.replace(source, repl1 + repl2), "TEST(TEST(1 + 2), val + val + val)") self.assertEqual(asttokens.util.replace(source, repl2 + repl1), "TEST(TEST(1 + 2), val + val + val)") def test_expect_token(): atok = asttokens.ASTTokens("a", parse=True) tok = atok.tokens[0] with pytest.raises(ValueError): asttokens.util.expect_token(tok, token.OP) def test_combine_tokens(): from tokenize import TokenInfo, generate_tokens, ERRORTOKEN, OP, NUMBER, NAME from asttokens.util import combine_tokens, patched_generate_tokens text = "℘·2=1" original_tokens = [] for tok in generate_tokens(io.StringIO(text).readline): original_tokens.append(tok) if tok.type == OP: break correct_tokens = [ TokenInfo(NAME, string='℘·2', start=(1, 0), end=(1, 3), line='℘·2=1'), TokenInfo(OP, string='=', start=(1, 3), end=(1, 4), line='℘·2=1'), ] if sys.version_info >= (3, 12): # The tokenizing bug was fixed in 3.12, so the original tokens are correct, # rather than starting with false ERRORTOKENs. assert original_tokens == correct_tokens else: assert original_tokens == [ TokenInfo(ERRORTOKEN, string='℘', start=(1, 0), end=(1, 1), line='℘·2=1'), TokenInfo(ERRORTOKEN, string='·', start=(1, 1), end=(1, 2), line='℘·2=1'), TokenInfo(NUMBER, string='2', start=(1, 2), end=(1, 3), line='℘·2=1'), TokenInfo(OP, string='=', start=(1, 3), end=(1, 4), line='℘·2=1'), ] assert combine_tokens(original_tokens[:1]) == [ TokenInfo(NAME, string='℘', start=(1, 0), end=(1, 1), line='℘·2=1'), ] assert combine_tokens(original_tokens[:2]) == [ TokenInfo(NAME, string='℘·', start=(1, 0), end=(1, 2), line='℘·2=1'), ] assert combine_tokens(original_tokens[:3]) == [ TokenInfo(NAME, string='℘·2', start=(1, 0), end=(1, 3), line='℘·2=1'), ] assert list(patched_generate_tokens(iter(original_tokens))) == correct_tokens assert list(patched_generate_tokens(iter(original_tokens[:-1]))) == [ TokenInfo(NAME, string='℘·2', start=(1, 0), end=(1, 3), line='℘·2=1'), ] if __name__ == "__main__": unittest.main() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4775593 asttokens-3.0.0/tests/testdata/0000755000076700000240000000000014722510216015524 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1496675903.0 asttokens-3.0.0/tests/testdata/README.md0000644000076700000240000000020513115273077017006 0ustar00develstaffEverything in astroid subdirectories comes from the astroid library's test suite, which cover a variety of Python syntax constructs. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4629607 asttokens-3.0.0/tests/testdata/python2/0000755000076700000240000000000014722510216017127 5ustar00develstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4830637 asttokens-3.0.0/tests/testdata/python2/astroid/0000755000076700000240000000000014722510216020574 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/__init__.py0000644000076700000240000000010414575366762022725 0ustar00develstaff__revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/absimport.py0000644000076700000240000000011614575366762023171 0ustar00develstafffrom __future__ import absolute_import import email from email import message ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/all.py0000644000076700000240000000015214575366762021741 0ustar00develstaff name = 'a' _bla = 2 other = 'o' class Aaa: pass def func(): print 'yo' __all__ = 'Aaa', '_bla', 'name' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/clientmodule_test.py0000644000076700000240000000144014575366762024715 0ustar00develstaff""" docstring for file clientmodule.py """ from data.suppliermodule_test import Interface as IFace, DoNothing class Toto: pass class Ancestor: """ Ancestor method """ __implements__ = (IFace,) def __init__(self, value): local_variable = 0 self.attr = 'this method shouldn\'t have a docstring' self.__value = value def get_value(self): """ nice docstring ;-) """ return self.__value def set_value(self, value): self.__value = value return 'this method shouldn\'t have a docstring' class Specialization(Ancestor): TYPE = 'final class' top = 'class' def __init__(self, value, _id): Ancestor.__init__(self, value) self._id = _id self.relation = DoNothing() self.toto = Toto() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/descriptor_crash.py0000644000076700000240000000033114575366762024526 0ustar00develstaff import urllib class Page(object): _urlOpen = staticmethod(urllib.urlopen) def getPage(self, url): handle = self._urlOpen(url) data = handle.read() handle.close() return data ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/email.py0000644000076700000240000000010614575366762022257 0ustar00develstaff"""fake email module to test absolute import doesn't grab this one""" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/format.py0000644000076700000240000000064514575366762022470 0ustar00develstaff"""A multiline string """ function('aeozrijz\ earzer', hop) # XXX write test x = [i for i in range(5) if i % 4] fonction(1, 2, 3, 4) def definition(a, b, c): return a + b + c class debile(dict, object): pass if aaaa: pass else: aaaa,bbbb = 1,2 aaaa,bbbb = bbbb,aaaa # XXX write test hop = \ aaaa __revision__.lower(); ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/joined_strings.py0000644000076700000240000021475014575366762024225 0ustar00develstaffx = ('R0lGODlhigJnAef/AAABAAEEAAkCAAMGAg0GBAYJBQoMCBMODQ4QDRITEBkS' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7')././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/module.py0000644000076700000240000000341414575366762022462 0ustar00develstaff"""test module for astroid """ __revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $' from astroid.node_classes import Name as NameNode from astroid import modutils from astroid.utils import * import os.path MY_DICT = {} def global_access(key, val): """function test""" local = 1 MY_DICT[key] = val for i in val: if i: del MY_DICT[i] continue else: break else: return local class YO: """hehe""" a = 1 def __init__(self): try: self.yo = 1 except ValueError, ex: pass except (NameError, TypeError): raise XXXError() except: raise class YOUPI(YO): class_attr = None def __init__(self): self.member = None def method(self): """method test""" global MY_DICT try: MY_DICT = {} local = None autre = [a for (a, b) in MY_DICT if b] if b in autre: return b else: if a in autre: return a global_access(local, val=autre) finally: return local def static_method(): """static method test""" assert MY_DICT, '???' static_method = staticmethod(static_method) def class_method(cls): """class method test""" exec a in b class_method = classmethod(class_method) def four_args(a, b, c, d): """four arguments (was nested_args)""" pass while 1: if a: break a += +1 else: b += -2 if c: d = ((a) and (b)) or (c) else: c = ((a) and (b)) or (d) map(lambda x, y: (y, x), a) redirect = four_args ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/module2.py0000644000076700000240000000365014575366762022546 0ustar00develstafffrom data.module import YO, YOUPI import data class Specialization(YOUPI, YO): pass class Metaclass(type): pass class Interface: pass class MyIFace(Interface): pass class AnotherIFace(Interface): pass class MyException(Exception): pass class MyError(MyException): pass class AbstractClass(object): def to_override(self, whatever): raise NotImplementedError() def return_something(self, param): if param: return 'toto' return class Concrete0: __implements__ = MyIFace class Concrete1: __implements__ = (MyIFace, AnotherIFace) class Concrete2: __implements__ = (MyIFace, AnotherIFace) class Concrete23(Concrete1): pass del YO.member del YO [SYN1, SYN2] = (Concrete0, Concrete1) assert '1' b = (1) | (((2) & (3)) ^ (8)) bb = ((1) | (two)) | (6) ccc = ((one) & (two)) & (three) dddd = ((x) ^ (o)) ^ (r) exec 'c = 3' exec 'c = 3' in {}, {} def raise_string(a=2, *args, **kwargs): raise Exception, 'yo' yield 'coucou' yield a = (b) + (2) c = (b) * (2) c = (b) / (2) c = (b) // (2) c = (b) - (2) c = (b) % (2) c = (b) ** (2) c = (b) << (2) c = (b) >> (2) c = ~b c = not b d = [c] e = d[:] e = d[a:b:c] raise_string(*args, **kwargs) print >> stream, 'bonjour' print >> stream, 'salut', def make_class(any, base=data.module.YO, *args, **kwargs): """check base is correctly resolved to Concrete0""" class Aaaa(base): """dynamic class""" return Aaaa from os.path import abspath import os as myos class A: pass class A(A): pass def generator(): """A generator.""" yield def not_a_generator(): """A function that contains generator, but is not one.""" def generator(): yield genl = lambda : (yield) def with_metaclass(meta, *bases): return meta('NewBase', bases, {}) class NotMetaclass(with_metaclass(Metaclass)): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/noendingnewline.py0000644000076700000240000000076714575366762024370 0ustar00develstaffimport unittest class TestCase(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) def tearDown(self): unittest.TestCase.tearDown(self) def testIt(self): self.a = 10 self.xxx() def xxx(self): if False: pass print 'a' if False: pass pass if False: pass print 'rara' if __name__ == '__main__': print 'test2' unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/nonregr.py0000644000076700000240000000217614575366762022653 0ustar00develstafffrom __future__ import generators, print_function try: enumerate = enumerate except NameError: def enumerate(iterable): """emulates the python2.3 enumerate() function""" i = 0 for val in iterable: yield i, val i += 1 def toto(value): for k, v in value: print(v.get('yo')) import imp fp, mpath, desc = imp.find_module('optparse',a) s_opt = imp.load_module('std_optparse', fp, mpath, desc) class OptionParser(s_opt.OptionParser): def parse_args(self, args=None, values=None, real_optparse=False): if real_optparse: pass ## return super(OptionParser, self).parse_args() else: import optcomp optcomp.completion(self) class Aaa(object): """docstring""" def __init__(self): self.__setattr__('a','b') pass def one_public(self): """docstring""" pass def another_public(self): """docstring""" pass class Ccc(Aaa): """docstring""" class Ddd(Aaa): """docstring""" pass class Eee(Ddd): """docstring""" pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/notall.py0000644000076700000240000000011214575366762022456 0ustar00develstaffname = 'a' _bla = 2 other = 'o' class Aaa: pass def func(): print('yo') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/recursion.py0000644000076700000240000000006414575366762023204 0ustar00develstaff""" For issue #25 """ class Base(object): pass././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1710616050.0 asttokens-3.0.0/tests/testdata/python2/astroid/suppliermodule_test.py0000644000076700000240000000035414575366762025305 0ustar00develstaff""" file suppliermodule.py """ class NotImplemented(Exception): pass class Interface: def get_value(self): raise NotImplemented() def set_value(self, value): raise NotImplemented() class DoNothing : pass ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1732939918.4631665 asttokens-3.0.0/tests/testdata/python3/0000755000076700000240000000000014722510216017130 5ustar00develstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1732939918.486869 asttokens-3.0.0/tests/testdata/python3/astroid/0000755000076700000240000000000014722510216020575 5ustar00develstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/__init__.py0000644000076700000240000000010413022720573022702 0ustar00develstaff__revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/absimport.py0000644000076700000240000000005013022720573023143 0ustar00develstaff import email from email import message ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/all.py0000644000076700000240000000015313022720573021717 0ustar00develstaff name = 'a' _bla = 2 other = 'o' class Aaa: pass def func(): print('yo') __all__ = 'Aaa', '_bla', 'name' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/clientmodule_test.py0000644000076700000240000000144013022720573024672 0ustar00develstaff""" docstring for file clientmodule.py """ from data.suppliermodule_test import Interface as IFace, DoNothing class Toto: pass class Ancestor: """ Ancestor method """ __implements__ = (IFace,) def __init__(self, value): local_variable = 0 self.attr = 'this method shouldn\'t have a docstring' self.__value = value def get_value(self): """ nice docstring ;-) """ return self.__value def set_value(self, value): self.__value = value return 'this method shouldn\'t have a docstring' class Specialization(Ancestor): TYPE = 'final class' top = 'class' def __init__(self, value, _id): Ancestor.__init__(self, value) self._id = _id self.relation = DoNothing() self.toto = Toto() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/descriptor_crash.py0000644000076700000240000000033113022720573024503 0ustar00develstaff import urllib class Page(object): _urlOpen = staticmethod(urllib.urlopen) def getPage(self, url): handle = self._urlOpen(url) data = handle.read() handle.close() return data ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/email.py0000644000076700000240000000010613022720573022234 0ustar00develstaff"""fake email module to test absolute import doesn't grab this one""" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/format.py0000644000076700000240000000064513022720573022445 0ustar00develstaff"""A multiline string """ function('aeozrijz\ earzer', hop) # XXX write test x = [i for i in range(5) if i % 4] fonction(1, 2, 3, 4) def definition(a, b, c): return a + b + c class debile(dict, object): pass if aaaa: pass else: aaaa,bbbb = 1,2 aaaa,bbbb = bbbb,aaaa # XXX write test hop = \ aaaa __revision__.lower(); ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1530806037.0 asttokens-3.0.0/tests/testdata/python3/astroid/joined_strings.py0000644000076700000240000021475013317437425024212 0ustar00develstaffx = ('R0lGODlhigJnAef/AAABAAEEAAkCAAMGAg0GBAYJBQoMCBMODQ4QDRITEBkS' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7' +'CxsSEhkWDhYYFQ0aJhkaGBweGyccGh8hHiIkIiMmGTEiHhQoPSYoJSkqKDcp' +'Ii0uLDAxLzI0Mh44U0gxMDI5JkM0JjU3NDY6Kjc5Njo7OUE8Ozw+Oz89QTxA' +'F1akOFFiRIgPHTZksKBAgMCLGTdGNIAAQgKfDAcgZbj0odOnUA8GBAA7')././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/module.py0000644000076700000240000000340713022720573022441 0ustar00develstaff"""test module for astroid """ __revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $' from astroid.node_classes import Name as NameNode from astroid import modutils from astroid.utils import * import os.path MY_DICT = {} def global_access(key, val): """function test""" local = 1 MY_DICT[key] = val for i in val: if i: del MY_DICT[i] continue else: break else: return class YO: """hehe""" a = 1 def __init__(self): try: self.yo = 1 except ValueError as ex: pass except (NameError, TypeError): raise XXXError() except: raise class YOUPI(YO): class_attr = None def __init__(self): self.member = None def method(self): """method test""" global MY_DICT try: MY_DICT = {} local = None autre = [a for (a, b) in MY_DICT if b] if b in autre: return else: if a in autre: return 'hehe' global_access(local, val=autre) finally: return local def static_method(): """static method test""" assert MY_DICT, '???' static_method = staticmethod(static_method) def class_method(cls): """class method test""" exec(a, b) class_method = classmethod(class_method) def four_args(a, b, c, d): """four arguments (was nested_args)""" while 1: if a: break a += +1 else: b += -2 if c: d = ((a) and (b)) or (c) else: c = ((a) and (b)) or (d) list(map(lambda x, y: (y, x), a)) redirect = four_args ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/module2.py0000644000076700000240000000367213022720573022527 0ustar00develstafffrom data.module import YO, YOUPI import data class Specialization(YOUPI, YO): pass class Metaclass(type): pass class Interface: pass class MyIFace(Interface): pass class AnotherIFace(Interface): pass class MyException(Exception): pass class MyError(MyException): pass class AbstractClass(object): def to_override(self, whatever): raise NotImplementedError() def return_something(self, param): if param: return 'toto' return class Concrete0: __implements__ = MyIFace class Concrete1: __implements__ = (MyIFace, AnotherIFace) class Concrete2: __implements__ = (MyIFace, AnotherIFace) class Concrete23(Concrete1): pass del YO.member del YO [SYN1, SYN2] = (Concrete0, Concrete1) assert repr(1) b = (1) | (((2) & (3)) ^ (8)) bb = ((1) | (two)) | (6) ccc = ((one) & (two)) & (three) dddd = ((x) ^ (o)) ^ (r) exec('c = 3') exec('c = 3', {}, {}) def raise_string(a=2, *args, **kwargs): raise Exception('yo') yield 'coucou' yield a = (b) + (2) c = (b) * (2) c = (b) / (2) c = (b) // (2) c = (b) - (2) c = (b) % (2) c = (b) ** (2) c = (b) << (2) c = (b) >> (2) c = ~b c = not b d = [c] e = d[:] e = d[a:b:c] raise_string(*args, **kwargs) print('bonjour', file=stream) print('salut', end=' ', file=stream) def make_class(any, base=data.module.YO, *args, **kwargs): """check base is correctly resolved to Concrete0""" class Aaaa(base): """dynamic class""" return Aaaa from os.path import abspath import os as myos class A: pass class A(A): pass def generator(): """A generator.""" yield def not_a_generator(): """A function that contains generator, but is not one.""" def generator(): yield genl = lambda : (yield) def with_metaclass(meta, *bases): return meta('NewBase', bases, {}) class NotMetaclass(with_metaclass(Metaclass)): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/noendingnewline.py0000644000076700000240000000077213022720573024341 0ustar00develstaffimport unittest class TestCase(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self) def tearDown(self): unittest.TestCase.tearDown(self) def testIt(self): self.a = 10 self.xxx() def xxx(self): if False: pass print('a') if False: pass pass if False: pass print('rara') if __name__ == '__main__': print('test2') unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/nonregr.py0000644000076700000240000000211513022720573022621 0ustar00develstaff try: enumerate = enumerate except NameError: def enumerate(iterable): """emulates the python2.3 enumerate() function""" i = 0 for val in iterable: yield i, val i += 1 def toto(value): for k, v in value: print(v.get('yo')) import imp fp, mpath, desc = imp.find_module('optparse',a) s_opt = imp.load_module('std_optparse', fp, mpath, desc) class OptionParser(s_opt.OptionParser): def parse_args(self, args=None, values=None, real_optparse=False): if real_optparse: pass ## return super(OptionParser, self).parse_args() else: import optcomp optcomp.completion(self) class Aaa(object): """docstring""" def __init__(self): self.__setattr__('a','b') pass def one_public(self): """docstring""" pass def another_public(self): """docstring""" pass class Ccc(Aaa): """docstring""" class Ddd(Aaa): """docstring""" pass class Eee(Ddd): """docstring""" pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/notall.py0000644000076700000240000000011313022720573022434 0ustar00develstaff name = 'a' _bla = 2 other = 'o' class Aaa: pass def func(): print('yo') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/recursion.py0000644000076700000240000000006413022720573023161 0ustar00develstaff""" For issue #25 """ class Base(object): pass././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1481351547.0 asttokens-3.0.0/tests/testdata/python3/astroid/suppliermodule_test.py0000644000076700000240000000035413022720573025262 0ustar00develstaff""" file suppliermodule.py """ class NotImplemented(Exception): pass class Interface: def get_value(self): raise NotImplemented() def set_value(self, value): raise NotImplemented() class DoNothing : pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/tests/tools.py0000644000076700000240000002070214722467250015437 0ustar00develstaffimport ast import io import os import re import sys import astroid from asttokens import util, supports_tokenless, ASTText from asttokens.util import is_ellipsis, is_expr_stmt def get_fixture_path(*path_parts): python_dir = 'python%s' % sys.version_info[0] return os.path.join(os.path.dirname(__file__), "testdata", python_dir, *path_parts) def read_fixture(*path_parts): with io.open(get_fixture_path(*path_parts), "r", newline="\n") as f: return f.read() def collect_nodes_preorder(root): """Returns a list of all nodes using pre-order traversal (i.e. parent before children).""" nodes = [] def append(node, par_value): # pylint: disable=unused-argument nodes.append(node) return (None, None) util.visit_tree(root, append, None) return nodes def get_node_name(node): name = node.__class__.__name__ return 'Constant' if name in ('Num', 'Str', 'NameConstant') else name class MarkChecker(object): """ Helper tool to parse and mark an AST tree, with useful methods for verifying it. """ def __init__(self, atok): self.atok = atok self.all_nodes = collect_nodes_preorder(self.atok.tree) self.atext = ASTText(atok.text, atok.tree, atok.filename) def get_nodes_at(self, line, col): """Returns all nodes that start with the token at the given position.""" token = self.atok.get_token(line, col) return [n for n in self.all_nodes if n.first_token == token] def view_node(self, node): """Returns a representation of a node and its text, such as "Call:foo()". """ return "%s:%s" % (get_node_name(node), self.atok.get_text(node)) def view_nodes_at(self, line, col): """ Returns a set of all node representations for nodes that start at the given position. E.g. {"Call:foo()", "Name:foo"} """ return {self.view_node(n) for n in self.get_nodes_at(line, col)} def view_node_types_at(self, line, col): """ Returns a set of all node types for nodes that start at the given position. E.g. {"Call", "Name"} """ return {n.__class__.__name__ for n in self.get_nodes_at(line, col)} def verify_all_nodes(self, test_case): """ Generically test atok.get_text() on the ast tree: for each statement and expression in the tree, we extract the text, parse it, and see if it produces an equivalent tree. Returns the number of nodes that were tested this way. """ test_case.longMessage = True if supports_tokenless() and not test_case.is_astroid_test: num_supported = sum(supports_tokenless(n) for n in self.all_nodes) num_nodes = len(self.all_nodes) test_case.assertGreater(num_supported / num_nodes, 0.5, (num_supported, num_nodes)) tested_nodes = 0 for node in self.all_nodes: # slices currently only get the correct tokens/text for ast, not astroid. if util.is_slice(node) and test_case.is_astroid_test: continue text = self.atok.get_text(node) self.check_get_text_tokenless(node, test_case, text) if not ( util.is_stmt(node) or util.is_expr(node) or util.is_module(node)): continue # await is not allowed outside async functions below 3.7 # parsing again would give a syntax error if 'await' in text and 'async def' not in text and sys.version_info < (3, 7): continue # `elif:` is really just `else: if:` to the AST, # so get_text can return text starting with elif when given an If node. # This is generally harmless and there's probably no good alternative, # but in isolation it's invalid syntax text = re.sub(r'^(\s*)elif(\W)', r'\1if\2', text, re.MULTILINE) rebuilt_node = test_case.parse_snippet(text, node) try: test_case.assert_nodes_equal(node, rebuilt_node) except AssertionError: if test_case.is_astroid_test: # This can give a more helpful failure message with a diff test_case.assertEqual( repr_tree(node), repr_tree(rebuilt_node), ) raise tested_nodes += 1 return tested_nodes def check_get_text_tokenless(self, node, test_case, text): """ Check that `text` (returned from get_text()) usually returns the same text whether from `ASTTokens` or `ASTText`. """ if not supports_tokenless(): return text_tokenless = self.atext.get_text(node) if isinstance(node, ast.alias): self._check_alias_tokenless(node, test_case, text_tokenless) elif util.is_module(node): test_case.assertEqual(text_tokenless, self.atext._text) elif isinstance(node, astroid.DictUnpack): # This is a strange node that *seems* to represent just the `**` in `{**foo}` # (not `**foo` or `foo`), but text_tokenless is `foo` # while `text` is just the first token of that. # 'Fixing' either of these or making them match doesn't seem useful. return elif isinstance(node, astroid.Decorators): # Another strange node where it's not worth making the two texts match return elif supports_tokenless(node): has_lineno = getattr(node, 'lineno', None) is not None test_case.assertEqual(has_lineno, text_tokenless != '') if has_lineno: if text != text_tokenless: if ( text_tokenless.startswith(text) and test_case.is_astroid_test and ( # astroid positions can include type comments, which we can ignore. text_tokenless[len(text):].strip().startswith('# type: ') # astroid+ASTTokens doesn't correctly handle the 3.12+ type variable syntax. # Since ASTText is preferred for new Python versions, this is not a priority. or isinstance(node.parent, astroid.TypeVar) ) ): return if ( text == text_tokenless.lstrip() and isinstance(getattr(node, 'body', None), list) and len(node.body) == 1 and is_expr_stmt(node.body[0]) and is_ellipsis(node.body[0].value) ): # ASTTokens doesn't include padding for compound statements where the # body is a single statement starting on the same line where the header ends. # ASTText does include padding in this case if the header spans multiple lines, # as does ast.get_source_segment(padded=True). # This is a minor difference and not worth fixing. # In practice it arises in test_sys_modules when testing files containing # function definition stubs like: # def foo( # # ): ... # (actual ellipsis) return test_case.assertEqual(text, text_tokenless) else: # _get_text_positions_tokenless can't work with nodes without lineno. # Double-check that such nodes are unusual. test_case.assertFalse(util.is_stmt(node) or util.is_expr(node)) if not test_case.is_astroid_test: with test_case.assertRaises(SyntaxError, msg=(text, ast.dump(node))): test_case.parse_snippet(text, node) def _check_alias_tokenless(self, node, test_case, text): if sys.version_info < (3, 10): # Before 3.10, aliases don't have position information test_case.assertEqual(text, '') # For 3.10+, ASTTokens.get_text often returns the wrong value for aliases. # So to verify ASTText.get_text, we instead check the general form. elif node.asname: test_case.assertEqual(text.split(), [node.name, 'as', node.asname]) else: test_case.assertEqual(text, node.name) def repr_tree(node): """ Returns a canonical string representation of an astroid node normalised to ignore the context of each node which can change when parsing substrings of source code. E.g. "a" is a Name in expression "a + 1" and is an AssignName in expression "a = 1", but we don't care about this difference when comparing structure and content. """ result = node.repr_tree() # astroid represents context in multiple ways # Convert Store and Del contexts to Load # Similarly convert Assign/Del Name/Attr to just Name/Attribute (i.e. Load) result = re.sub(r'(AssignName|DelName)(\(\s*name=)', r'Name\2', result) result = re.sub(r'(AssignAttr|DelAttr)(\(\s*attrname=)', r'Attribute\2', result) result = re.sub(r'ctx=', r'ctx=', result) # Weird bug in astroid that collapses spaces in docstrings sometimes maybe result = re.sub(r"' +\\n'", r"'\\n'", result) return result ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1732931240.0 asttokens-3.0.0/tox.ini0000644000076700000240000000053314722467250014076 0ustar00develstaff# Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. [tox] envlist = py{38,39,310,311,312,313,py3} [testenv] commands = pytest {posargs} deps = .[test]