From 25bcaa05a10ea09fdf3eab87c0dfd22e78a01ac7 Mon Sep 17 00:00:00 2001 From: Yosef Levy Date: Tue, 4 Jul 2017 21:39:08 +0300 Subject: [PATCH 1/2] add two tests and travis --- .cache/v/cache/lastfailed | 1 + .travis.yml | 9 + tests/test_anagram.py | 10 + venv/bin/activate | 76 + venv/bin/activate.csh | 37 + venv/bin/activate.fish | 74 + venv/bin/easy_install | 11 + venv/bin/easy_install-3.5 | 11 + venv/bin/pip | 11 + venv/bin/pip3 | 11 + venv/bin/pip3.5 | 11 + venv/bin/py.test | 11 + venv/bin/pytest | 11 + venv/bin/python | 1 + venv/bin/python3 | 1 + venv/bin/python3.5 | 1 + .../site-packages/_pytest/__init__.py | 8 + .../site-packages/_pytest/_argcomplete.py | 102 + .../site-packages/_pytest/_code/__init__.py | 10 + .../_pytest/_code/_py2traceback.py | 82 + .../site-packages/_pytest/_code/code.py | 895 ++++ .../site-packages/_pytest/_code/source.py | 414 ++ .../site-packages/_pytest/_pluggy.py | 11 + .../site-packages/_pytest/_version.py | 4 + .../_pytest/assertion/__init__.py | 149 + .../_pytest/assertion/rewrite.py | 941 ++++ .../_pytest/assertion/truncate.py | 102 + .../site-packages/_pytest/assertion/util.py | 301 ++ .../site-packages/_pytest/cacheprovider.py | 245 ++ .../site-packages/_pytest/capture.py | 542 +++ .../python3.5/site-packages/_pytest/compat.py | 307 ++ .../python3.5/site-packages/_pytest/config.py | 1397 ++++++ .../site-packages/_pytest/debugging.py | 123 + .../site-packages/_pytest/deprecated.py | 24 + .../site-packages/_pytest/doctest.py | 360 ++ .../site-packages/_pytest/fixtures.py | 1129 +++++ .../site-packages/_pytest/freeze_support.py | 44 + .../site-packages/_pytest/helpconfig.py | 179 + .../site-packages/_pytest/hookspec.py | 353 ++ .../site-packages/_pytest/junitxml.py | 452 ++ .../python3.5/site-packages/_pytest/main.py | 784 ++++ .../python3.5/site-packages/_pytest/mark.py | 391 ++ .../site-packages/_pytest/monkeypatch.py | 259 ++ .../python3.5/site-packages/_pytest/nose.py | 72 + .../site-packages/_pytest/pastebin.py | 100 + .../site-packages/_pytest/pytester.py | 1151 +++++ .../python3.5/site-packages/_pytest/python.py | 1597 +++++++ .../site-packages/_pytest/recwarn.py | 204 + .../site-packages/_pytest/resultlog.py | 108 + .../python3.5/site-packages/_pytest/runner.py | 580 +++ .../site-packages/_pytest/setuponly.py | 74 + .../site-packages/_pytest/setupplan.py | 25 + .../site-packages/_pytest/skipping.py | 380 ++ .../site-packages/_pytest/terminal.py | 627 +++ .../python3.5/site-packages/_pytest/tmpdir.py | 126 + .../site-packages/_pytest/unittest.py | 239 ++ .../_pytest/vendored_packages/__init__.py | 0 .../_pytest/vendored_packages/pluggy.py | 802 ++++ .../site-packages/_pytest/warnings.py | 88 + .../python3.5/site-packages/easy_install.py | 5 + .../pip-8.1.1.dist-info/DESCRIPTION.rst | 36 + .../pip-8.1.1.dist-info/INSTALLER | 1 + .../pip-8.1.1.dist-info/METADATA | 65 + .../site-packages/pip-8.1.1.dist-info/RECORD | 117 + .../site-packages/pip-8.1.1.dist-info/WHEEL | 6 + .../pip-8.1.1.dist-info/entry_points.txt | 5 + .../pip-8.1.1.dist-info/metadata.json | 1 + .../pip-8.1.1.dist-info/top_level.txt | 1 + .../python3.5/site-packages/pip/__init__.py | 315 ++ .../python3.5/site-packages/pip/__main__.py | 19 + .../site-packages/pip/_vendor/__init__.py | 110 + .../site-packages/pip/basecommand.py | 325 ++ .../python3.5/site-packages/pip/baseparser.py | 292 ++ .../python3.5/site-packages/pip/cmdoptions.py | 618 +++ .../site-packages/pip/commands/__init__.py | 83 + .../site-packages/pip/commands/completion.py | 67 + .../site-packages/pip/commands/download.py | 136 + .../site-packages/pip/commands/freeze.py | 86 + .../site-packages/pip/commands/hash.py | 57 + .../site-packages/pip/commands/help.py | 35 + .../site-packages/pip/commands/install.py | 404 ++ .../site-packages/pip/commands/list.py | 209 + .../site-packages/pip/commands/search.py | 146 + .../site-packages/pip/commands/show.py | 154 + .../site-packages/pip/commands/uninstall.py | 76 + .../site-packages/pip/commands/wheel.py | 204 + .../site-packages/pip/compat/__init__.py | 164 + .../site-packages/pip/compat/dictconfig.py | 565 +++ .../site-packages/pip/compat/ordereddict.py | 129 + .../python3.5/site-packages/pip/download.py | 895 ++++ .../python3.5/site-packages/pip/exceptions.py | 239 ++ venv/lib/python3.5/site-packages/pip/index.py | 1048 +++++ .../python3.5/site-packages/pip/locations.py | 182 + .../site-packages/pip/models/__init__.py | 4 + .../site-packages/pip/models/index.py | 16 + .../site-packages/pip/operations/__init__.py | 0 .../site-packages/pip/operations/freeze.py | 122 + .../python3.5/site-packages/pip/pep425tags.py | 338 ++ .../site-packages/pip/req/__init__.py | 10 + .../site-packages/pip/req/req_file.py | 342 ++ .../site-packages/pip/req/req_install.py | 1190 ++++++ .../site-packages/pip/req/req_set.py | 746 ++++ .../site-packages/pip/req/req_uninstall.py | 195 + .../site-packages/pip/status_codes.py | 8 + .../site-packages/pip/utils/__init__.py | 878 ++++ .../site-packages/pip/utils/appdirs.py | 224 + .../site-packages/pip/utils/build.py | 42 + .../site-packages/pip/utils/deprecation.py | 76 + .../site-packages/pip/utils/encoding.py | 31 + .../site-packages/pip/utils/filesystem.py | 28 + .../site-packages/pip/utils/hashes.py | 92 + .../site-packages/pip/utils/logging.py | 130 + .../site-packages/pip/utils/outdated.py | 162 + .../pip/utils/setuptools_build.py | 6 + .../python3.5/site-packages/pip/utils/ui.py | 344 ++ .../site-packages/pip/vcs/__init__.py | 363 ++ .../python3.5/site-packages/pip/vcs/bazaar.py | 116 + .../python3.5/site-packages/pip/vcs/git.py | 277 ++ .../site-packages/pip/vcs/mercurial.py | 103 + .../site-packages/pip/vcs/subversion.py | 249 ++ venv/lib/python3.5/site-packages/pip/wheel.py | 855 ++++ .../DESCRIPTION.rst | 3 + .../pkg_resources-0.0.0.dist-info/INSTALLER | 1 + .../pkg_resources-0.0.0.dist-info/METADATA | 13 + .../pkg_resources-0.0.0.dist-info/RECORD | 34 + .../pkg_resources-0.0.0.dist-info/WHEEL | 6 + .../metadata.json | 1 + .../site-packages/pkg_resources/__init__.py | 2956 +++++++++++++ .../pkg_resources/_vendor/__init__.py | 0 .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 278 ++ .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 ++++ .../pkg_resources/_vendor/packaging/utils.py | 14 + .../_vendor/packaging/version.py | 393 ++ .../pkg_resources/_vendor/pyparsing.py | 3805 +++++++++++++++++ .../pkg_resources/_vendor/six.py | 868 ++++ .../pkg_resources/extern/__init__.py | 71 + .../py-1.4.34.dist-info/DESCRIPTION.rst | 23 + .../py-1.4.34.dist-info/INSTALLER | 1 + .../py-1.4.34.dist-info/LICENSE.txt | 19 + .../py-1.4.34.dist-info/METADATA | 48 + .../site-packages/py-1.4.34.dist-info/RECORD | 74 + .../site-packages/py-1.4.34.dist-info/WHEEL | 6 + .../py-1.4.34.dist-info/metadata.json | 1 + .../py-1.4.34.dist-info/top_level.txt | 1 + .../python3.5/site-packages/py/__init__.py | 152 + .../python3.5/site-packages/py/__metainfo.py | 2 + .../lib/python3.5/site-packages/py/_apipkg.py | 181 + .../python3.5/site-packages/py/_builtin.py | 248 ++ .../site-packages/py/_code/__init__.py | 1 + .../site-packages/py/_code/_assertionnew.py | 339 ++ .../site-packages/py/_code/_assertionold.py | 555 +++ .../site-packages/py/_code/_py2traceback.py | 79 + .../site-packages/py/_code/assertion.py | 94 + .../python3.5/site-packages/py/_code/code.py | 787 ++++ .../site-packages/py/_code/source.py | 411 ++ venv/lib/python3.5/site-packages/py/_error.py | 89 + .../python3.5/site-packages/py/_iniconfig.py | 162 + .../site-packages/py/_io/__init__.py | 1 + .../python3.5/site-packages/py/_io/capture.py | 371 ++ .../site-packages/py/_io/saferepr.py | 71 + .../site-packages/py/_io/terminalwriter.py | 357 ++ .../site-packages/py/_log/__init__.py | 2 + .../python3.5/site-packages/py/_log/log.py | 186 + .../site-packages/py/_log/warning.py | 76 + .../site-packages/py/_path/__init__.py | 1 + .../site-packages/py/_path/cacheutil.py | 114 + .../site-packages/py/_path/common.py | 445 ++ .../python3.5/site-packages/py/_path/local.py | 930 ++++ .../site-packages/py/_path/svnurl.py | 380 ++ .../python3.5/site-packages/py/_path/svnwc.py | 1240 ++++++ .../site-packages/py/_process/__init__.py | 1 + .../site-packages/py/_process/cmdexec.py | 49 + .../site-packages/py/_process/forkedfunc.py | 120 + .../site-packages/py/_process/killproc.py | 23 + venv/lib/python3.5/site-packages/py/_std.py | 18 + .../lib/python3.5/site-packages/py/_xmlgen.py | 255 ++ venv/lib/python3.5/site-packages/py/test.py | 10 + .../pytest-3.1.3.dist-info/DESCRIPTION.rst | 111 + .../pytest-3.1.3.dist-info/INSTALLER | 1 + .../pytest-3.1.3.dist-info/LICENSE.txt | 21 + .../pytest-3.1.3.dist-info/METADATA | 147 + .../pytest-3.1.3.dist-info/RECORD | 99 + .../pytest-3.1.3.dist-info/WHEEL | 6 + .../pytest-3.1.3.dist-info/entry_points.txt | 4 + .../pytest-3.1.3.dist-info/metadata.json | 1 + .../pytest-3.1.3.dist-info/top_level.txt | 2 + venv/lib/python3.5/site-packages/pytest.py | 78 + .../DESCRIPTION.rst | 238 ++ .../setuptools-20.7.0.dist-info/INSTALLER | 1 + .../setuptools-20.7.0.dist-info/METADATA | 263 ++ .../setuptools-20.7.0.dist-info/RECORD | 100 + .../setuptools-20.7.0.dist-info/WHEEL | 6 + .../dependency_links.txt | 2 + .../entry_points.txt | 61 + .../setuptools-20.7.0.dist-info/metadata.json | 1 + .../setuptools-20.7.0.dist-info/top_level.txt | 3 + .../setuptools-20.7.0.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 169 + .../site-packages/setuptools/archive_util.py | 170 + .../setuptools/command/__init__.py | 18 + .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 471 ++ .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 21 + .../setuptools/command/build_ext.py | 296 ++ .../setuptools/command/build_py.py | 222 + .../setuptools/command/develop.py | 196 + .../setuptools/command/easy_install.py | 2315 ++++++++++ .../setuptools/command/egg_info.py | 482 +++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 138 + .../setuptools/command/install_lib.py | 147 + .../setuptools/command/install_scripts.py | 60 + .../setuptools/command/register.py | 10 + .../setuptools/command/rotate.py | 62 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 196 + .../setuptools/command/setopt.py | 150 + .../site-packages/setuptools/command/test.py | 196 + .../setuptools/command/upload.py | 23 + .../setuptools/command/upload_docs.py | 191 + .../site-packages/setuptools/depends.py | 217 + .../site-packages/setuptools/dist.py | 872 ++++ .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 5 + .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 58 + .../site-packages/setuptools/msvc9_support.py | 63 + .../site-packages/setuptools/package_index.py | 1069 +++++ .../site-packages/setuptools/py26compat.py | 22 + .../site-packages/setuptools/py27compat.py | 15 + .../site-packages/setuptools/py31compat.py | 52 + .../site-packages/setuptools/sandbox.py | 496 +++ .../setuptools/script (dev).tmpl | 5 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 76 + .../site-packages/setuptools/ssl_support.py | 243 ++ .../site-packages/setuptools/unicode_utils.py | 43 + .../site-packages/setuptools/utils.py | 11 + .../site-packages/setuptools/version.py | 6 + .../setuptools/windows_support.py | 29 + venv/lib64 | 1 + venv/pip-selfcheck.json | 1 + venv/pyvenv.cfg | 3 + .../CacheControl-0.11.5-py2.py3-none-any.whl | Bin 0 -> 17006 bytes .../chardet-2.3.0-py2.py3-none-any.whl | Bin 0 -> 180893 bytes .../colorama-0.3.7-py2.py3-none-any.whl | Bin 0 -> 19917 bytes .../distlib-0.2.2-py2.py3-none-any.whl | Bin 0 -> 139349 bytes .../html5lib-0.999-py2.py3-none-any.whl | Bin 0 -> 108385 bytes .../ipaddress-0.0.0-py2.py3-none-any.whl | Bin 0 -> 17454 bytes .../lockfile-0.12.2-py2.py3-none-any.whl | Bin 0 -> 13504 bytes .../packaging-16.6-py2.py3-none-any.whl | Bin 0 -> 22701 bytes .../pip-8.1.1-py2.py3-none-any.whl | Bin 0 -> 151984 bytes .../pkg_resources-0.0.0-py2.py3-none-any.whl | Bin 0 -> 91824 bytes .../progress-1.2-py2.py3-none-any.whl | Bin 0 -> 9605 bytes .../pyparsing-2.0.3-py2.py3-none-any.whl | Bin 0 -> 37692 bytes .../requests-2.9.1-py2.py3-none-any.whl | Bin 0 -> 72469 bytes .../retrying-1.3.3-py2.py3-none-any.whl | Bin 0 -> 9536 bytes .../setuptools-20.7.0-py2.py3-none-any.whl | Bin 0 -> 120358 bytes .../six-1.10.0-py2.py3-none-any.whl | Bin 0 -> 10342 bytes .../urllib3-1.13.1-py2.py3-none-any.whl | Bin 0 -> 83132 bytes .../wheel-0.29.0-py2.py3-none-any.whl | Bin 0 -> 65953 bytes 267 files changed, 57541 insertions(+) create mode 100644 .cache/v/cache/lastfailed create mode 100644 .travis.yml create mode 100644 tests/test_anagram.py create mode 100644 venv/bin/activate create mode 100644 venv/bin/activate.csh create mode 100644 venv/bin/activate.fish create mode 100755 venv/bin/easy_install create mode 100755 venv/bin/easy_install-3.5 create mode 100755 venv/bin/pip create mode 100755 venv/bin/pip3 create mode 100755 venv/bin/pip3.5 create mode 100755 venv/bin/py.test create mode 100755 venv/bin/pytest create mode 120000 venv/bin/python create mode 120000 venv/bin/python3 create mode 120000 venv/bin/python3.5 create mode 100644 venv/lib/python3.5/site-packages/_pytest/__init__.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_argcomplete.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_code/__init__.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_code/_py2traceback.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_code/code.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_code/source.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_pluggy.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/_version.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/assertion/__init__.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/assertion/rewrite.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/assertion/truncate.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/assertion/util.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/cacheprovider.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/capture.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/compat.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/config.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/debugging.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/deprecated.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/doctest.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/fixtures.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/freeze_support.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/helpconfig.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/hookspec.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/junitxml.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/main.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/mark.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/monkeypatch.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/nose.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/pastebin.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/pytester.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/python.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/recwarn.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/resultlog.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/runner.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/setuponly.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/setupplan.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/skipping.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/terminal.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/tmpdir.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/unittest.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/vendored_packages/__init__.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py create mode 100644 venv/lib/python3.5/site-packages/_pytest/warnings.py create mode 100644 venv/lib/python3.5/site-packages/easy_install.py create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/METADATA create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/RECORD create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/WHEEL create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/entry_points.txt create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/metadata.json create mode 100644 venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.5/site-packages/pip/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/__main__.py create mode 100644 venv/lib/python3.5/site-packages/pip/_vendor/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/basecommand.py create mode 100644 venv/lib/python3.5/site-packages/pip/baseparser.py create mode 100644 venv/lib/python3.5/site-packages/pip/cmdoptions.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/completion.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/download.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/freeze.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/hash.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/help.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/install.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/list.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/search.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/show.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/uninstall.py create mode 100644 venv/lib/python3.5/site-packages/pip/commands/wheel.py create mode 100644 venv/lib/python3.5/site-packages/pip/compat/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/compat/dictconfig.py create mode 100644 venv/lib/python3.5/site-packages/pip/compat/ordereddict.py create mode 100644 venv/lib/python3.5/site-packages/pip/download.py create mode 100644 venv/lib/python3.5/site-packages/pip/exceptions.py create mode 100644 venv/lib/python3.5/site-packages/pip/index.py create mode 100644 venv/lib/python3.5/site-packages/pip/locations.py create mode 100644 venv/lib/python3.5/site-packages/pip/models/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/models/index.py create mode 100644 venv/lib/python3.5/site-packages/pip/operations/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/operations/freeze.py create mode 100644 venv/lib/python3.5/site-packages/pip/pep425tags.py create mode 100644 venv/lib/python3.5/site-packages/pip/req/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/req/req_file.py create mode 100644 venv/lib/python3.5/site-packages/pip/req/req_install.py create mode 100644 venv/lib/python3.5/site-packages/pip/req/req_set.py create mode 100644 venv/lib/python3.5/site-packages/pip/req/req_uninstall.py create mode 100644 venv/lib/python3.5/site-packages/pip/status_codes.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/appdirs.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/build.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/deprecation.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/encoding.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/filesystem.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/hashes.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/logging.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/outdated.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/setuptools_build.py create mode 100644 venv/lib/python3.5/site-packages/pip/utils/ui.py create mode 100644 venv/lib/python3.5/site-packages/pip/vcs/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pip/vcs/bazaar.py create mode 100644 venv/lib/python3.5/site-packages/pip/vcs/git.py create mode 100644 venv/lib/python3.5/site-packages/pip/vcs/mercurial.py create mode 100644 venv/lib/python3.5/site-packages/pip/vcs/subversion.py create mode 100644 venv/lib/python3.5/site-packages/pip/wheel.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/METADATA create mode 100644 venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/RECORD create mode 100644 venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/WHEEL create mode 100644 venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/metadata.json create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py create mode 100644 venv/lib/python3.5/site-packages/pkg_resources/extern/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/INSTALLER create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/METADATA create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/RECORD create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/WHEEL create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/metadata.json create mode 100644 venv/lib/python3.5/site-packages/py-1.4.34.dist-info/top_level.txt create mode 100644 venv/lib/python3.5/site-packages/py/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py/__metainfo.py create mode 100644 venv/lib/python3.5/site-packages/py/_apipkg.py create mode 100644 venv/lib/python3.5/site-packages/py/_builtin.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/_assertionnew.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/_assertionold.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/_py2traceback.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/assertion.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/code.py create mode 100644 venv/lib/python3.5/site-packages/py/_code/source.py create mode 100644 venv/lib/python3.5/site-packages/py/_error.py create mode 100644 venv/lib/python3.5/site-packages/py/_iniconfig.py create mode 100644 venv/lib/python3.5/site-packages/py/_io/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py/_io/capture.py create mode 100644 venv/lib/python3.5/site-packages/py/_io/saferepr.py create mode 100644 venv/lib/python3.5/site-packages/py/_io/terminalwriter.py create mode 100644 venv/lib/python3.5/site-packages/py/_log/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py/_log/log.py create mode 100644 venv/lib/python3.5/site-packages/py/_log/warning.py create mode 100644 venv/lib/python3.5/site-packages/py/_path/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py/_path/cacheutil.py create mode 100644 venv/lib/python3.5/site-packages/py/_path/common.py create mode 100644 venv/lib/python3.5/site-packages/py/_path/local.py create mode 100644 venv/lib/python3.5/site-packages/py/_path/svnurl.py create mode 100644 venv/lib/python3.5/site-packages/py/_path/svnwc.py create mode 100644 venv/lib/python3.5/site-packages/py/_process/__init__.py create mode 100644 venv/lib/python3.5/site-packages/py/_process/cmdexec.py create mode 100644 venv/lib/python3.5/site-packages/py/_process/forkedfunc.py create mode 100644 venv/lib/python3.5/site-packages/py/_process/killproc.py create mode 100644 venv/lib/python3.5/site-packages/py/_std.py create mode 100644 venv/lib/python3.5/site-packages/py/_xmlgen.py create mode 100644 venv/lib/python3.5/site-packages/py/test.py create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/METADATA create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/RECORD create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/WHEEL create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/entry_points.txt create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/metadata.json create mode 100644 venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/top_level.txt create mode 100644 venv/lib/python3.5/site-packages/pytest.py create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/DESCRIPTION.rst create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/METADATA create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/RECORD create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/WHEEL create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/dependency_links.txt create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/metadata.json create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/zip-safe create mode 100644 venv/lib/python3.5/site-packages/setuptools/__init__.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/archive_util.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/__init__.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/alias.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/bdist_egg.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/bdist_rpm.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/bdist_wininst.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/build_ext.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/build_py.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/develop.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/easy_install.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/egg_info.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/install.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/install_egg_info.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/install_lib.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/install_scripts.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/register.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/rotate.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/saveopts.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/sdist.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/setopt.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/test.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/upload.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/command/upload_docs.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/depends.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/dist.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/extension.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/extern/__init__.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/launch.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/lib2to3_ex.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/msvc9_support.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/package_index.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/py26compat.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/py27compat.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/py31compat.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/sandbox.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/script (dev).tmpl create mode 100644 venv/lib/python3.5/site-packages/setuptools/script.tmpl create mode 100644 venv/lib/python3.5/site-packages/setuptools/site-patch.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/ssl_support.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/unicode_utils.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/utils.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/version.py create mode 100644 venv/lib/python3.5/site-packages/setuptools/windows_support.py create mode 120000 venv/lib64 create mode 100644 venv/pip-selfcheck.json create mode 100644 venv/pyvenv.cfg create mode 100644 venv/share/python-wheels/CacheControl-0.11.5-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/chardet-2.3.0-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/colorama-0.3.7-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/distlib-0.2.2-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/html5lib-0.999-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/ipaddress-0.0.0-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/packaging-16.6-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/pip-8.1.1-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/progress-1.2-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/pyparsing-2.0.3-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/requests-2.9.1-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/setuptools-20.7.0-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/six-1.10.0-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl create mode 100644 venv/share/python-wheels/wheel-0.29.0-py2.py3-none-any.whl diff --git a/.cache/v/cache/lastfailed b/.cache/v/cache/lastfailed new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.cache/v/cache/lastfailed @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f6c4d5a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: python +python: +- "3.5" +#install: +# - pip install . + +script: pytest + + diff --git a/tests/test_anagram.py b/tests/test_anagram.py new file mode 100644 index 0000000..ff5e4b3 --- /dev/null +++ b/tests/test_anagram.py @@ -0,0 +1,10 @@ +import sys +sys.path.append('.') +import mylib + + +def test_go(): + assert mylib.is_anagram("abc", "cba") == True + +def test_nogo(): + assert mylib.is_anagram("fbc", "cba") == False diff --git a/venv/bin/activate b/venv/bin/activate new file mode 100644 index 0000000..42eba12 --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelavent variables +deactivate nondestructive + +VIRTUAL_ENV="/home/yosi/co-learning-python-without-test/venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "$PYTHONHOME" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then + _OLD_VIRTUAL_PS1="$PS1" + if [ "x(venv) " != x ] ; then + PS1="(venv) $PS1" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r +fi diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 0000000..229729c --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelavent variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/yosi/co-learning-python-without-test/venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if ("venv" != "") then + set env_name = "venv" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish new file mode 100644 index 0000000..47ddc52 --- /dev/null +++ b/venv/bin/activate.fish @@ -0,0 +1,74 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + . ( begin + printf "function fish_prompt\n\t#" + functions _old_fish_prompt + end | psub ) + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelavent variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/yosi/co-learning-python-without-test/venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + . ( begin + printf "function _old_fish_prompt\n\t#" + functions fish_prompt + end | psub ) + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Prompt override? + if test -n "$(venv) " + printf "%s%s%s" "$(venv) " (set_color normal) (_old_fish_prompt) + return + end + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s %s" (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) (_old_fish_prompt) + else + printf "%s(%s)%s%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) (_old_fish_prompt) + end + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/venv/bin/easy_install b/venv/bin/easy_install new file mode 100755 index 0000000..8b4d1aa --- /dev/null +++ b/venv/bin/easy_install @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/easy_install-3.5 b/venv/bin/easy_install-3.5 new file mode 100755 index 0000000..8b4d1aa --- /dev/null +++ b/venv/bin/easy_install-3.5 @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 0000000..223bea9 --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3 b/venv/bin/pip3 new file mode 100755 index 0000000..223bea9 --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3.5 b/venv/bin/pip3.5 new file mode 100755 index 0000000..223bea9 --- /dev/null +++ b/venv/bin/pip3.5 @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/py.test b/venv/bin/py.test new file mode 100755 index 0000000..eb8a5c5 --- /dev/null +++ b/venv/bin/py.test @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pytest import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pytest b/venv/bin/pytest new file mode 100755 index 0000000..eb8a5c5 --- /dev/null +++ b/venv/bin/pytest @@ -0,0 +1,11 @@ +#!/home/yosi/co-learning-python-without-test/venv/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pytest import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 0000000..f549cea --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3.5 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 0000000..f549cea --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +python3.5 \ No newline at end of file diff --git a/venv/bin/python3.5 b/venv/bin/python3.5 new file mode 120000 index 0000000..baab9cb --- /dev/null +++ b/venv/bin/python3.5 @@ -0,0 +1 @@ +/usr/bin/python3.5 \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/_pytest/__init__.py b/venv/lib/python3.5/site-packages/_pytest/__init__.py new file mode 100644 index 0000000..6e41f05 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/__init__.py @@ -0,0 +1,8 @@ +__all__ = ['__version__'] + +try: + from ._version import version as __version__ +except ImportError: + # broken installation, we don't even try + # unknown only works because we do poor mans version compare + __version__ = 'unknown' diff --git a/venv/lib/python3.5/site-packages/_pytest/_argcomplete.py b/venv/lib/python3.5/site-packages/_pytest/_argcomplete.py new file mode 100644 index 0000000..8c93e4c --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_argcomplete.py @@ -0,0 +1,102 @@ + +"""allow bash-completion for argparse with argcomplete if installed +needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail +to find the magic string, so _ARGCOMPLETE env. var is never set, and +this does not need special code. + +argcomplete does not support python 2.5 (although the changes for that +are minor). + +Function try_argcomplete(parser) should be called directly before +the call to ArgumentParser.parse_args(). + +The filescompleter is what you normally would use on the positional +arguments specification, in order to get "dirname/" after "dirn" +instead of the default "dirname ": + + optparser.add_argument(Config._file_or_dir, nargs='*' + ).completer=filescompleter + +Other, application specific, completers should go in the file +doing the add_argument calls as they need to be specified as .completer +attributes as well. (If argcomplete is not installed, the function the +attribute points to will not be used). + +SPEEDUP +======= +The generic argcomplete script for bash-completion +(/etc/bash_completion.d/python-argcomplete.sh ) +uses a python program to determine startup script generated by pip. +You can speed up completion somewhat by changing this script to include + # PYTHON_ARGCOMPLETE_OK +so the the python-argcomplete-check-easy-install-script does not +need to be called to find the entry point of the code and see if that is +marked with PYTHON_ARGCOMPLETE_OK + +INSTALL/DEBUGGING +================= +To include this support in another application that has setup.py generated +scripts: +- add the line: + # PYTHON_ARGCOMPLETE_OK + near the top of the main python entry point +- include in the file calling parse_args(): + from _argcomplete import try_argcomplete, filescompleter + , call try_argcomplete just before parse_args(), and optionally add + filescompleter to the positional arguments' add_argument() +If things do not work right away: +- switch on argcomplete debugging with (also helpful when doing custom + completers): + export _ARC_DEBUG=1 +- run: + python-argcomplete-check-easy-install-script $(which appname) + echo $? + will echo 0 if the magic line has been found, 1 if not +- sometimes it helps to find early on errors using: + _ARGCOMPLETE=1 _ARC_DEBUG=1 appname + which should throw a KeyError: 'COMPLINE' (which is properly set by the + global argcomplete script). +""" +from __future__ import absolute_import, division, print_function +import sys +import os +from glob import glob + +class FastFilesCompleter: + 'Fast file completer class' + def __init__(self, directories=True): + self.directories = directories + + def __call__(self, prefix, **kwargs): + """only called on non option completions""" + if os.path.sep in prefix[1:]: # + prefix_dir = len(os.path.dirname(prefix) + os.path.sep) + else: + prefix_dir = 0 + completion = [] + globbed = [] + if '*' not in prefix and '?' not in prefix: + if prefix[-1] == os.path.sep: # we are on unix, otherwise no bash + globbed.extend(glob(prefix + '.*')) + prefix += '*' + globbed.extend(glob(prefix)) + for x in sorted(globbed): + if os.path.isdir(x): + x += '/' + # append stripping the prefix (like bash, not like compgen) + completion.append(x[prefix_dir:]) + return completion + + +if os.environ.get('_ARGCOMPLETE'): + try: + import argcomplete.completers + except ImportError: + sys.exit(-1) + filescompleter = FastFilesCompleter() + + def try_argcomplete(parser): + argcomplete.autocomplete(parser) +else: + def try_argcomplete(parser): pass + filescompleter = None diff --git a/venv/lib/python3.5/site-packages/_pytest/_code/__init__.py b/venv/lib/python3.5/site-packages/_pytest/_code/__init__.py new file mode 100644 index 0000000..815c13b --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_code/__init__.py @@ -0,0 +1,10 @@ +""" python inspection/code generation API """ +from __future__ import absolute_import, division, print_function +from .code import Code # noqa +from .code import ExceptionInfo # noqa +from .code import Frame # noqa +from .code import Traceback # noqa +from .code import getrawcode # noqa +from .source import Source # noqa +from .source import compile_ as compile # noqa +from .source import getfslineno # noqa diff --git a/venv/lib/python3.5/site-packages/_pytest/_code/_py2traceback.py b/venv/lib/python3.5/site-packages/_pytest/_code/_py2traceback.py new file mode 100644 index 0000000..d45ee01 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_code/_py2traceback.py @@ -0,0 +1,82 @@ +# copied from python-2.7.3's traceback.py +# CHANGES: +# - some_str is replaced, trying to create unicode strings +# +from __future__ import absolute_import, division, print_function +import types + +def format_exception_only(etype, value): + """Format the exception part of a traceback. + + The arguments are the exception type and value such as given by + sys.last_type and sys.last_value. The return value is a list of + strings, each ending in a newline. + + Normally, the list contains a single string; however, for + SyntaxError exceptions, it contains several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the list. + + """ + + # An instance should not have a meaningful value parameter, but + # sometimes does, particularly for string exceptions, such as + # >>> raise string1, string2 # deprecated + # + # Clear these out first because issubtype(string1, SyntaxError) + # would throw another exception and mask the original problem. + if (isinstance(etype, BaseException) or + isinstance(etype, types.InstanceType) or + etype is None or type(etype) is str): + return [_format_final_exc_line(etype, value)] + + stype = etype.__name__ + + if not issubclass(etype, SyntaxError): + return [_format_final_exc_line(stype, value)] + + # It was a syntax error; show exactly where the problem was found. + lines = [] + try: + msg, (filename, lineno, offset, badline) = value.args + except Exception: + pass + else: + filename = filename or "" + lines.append(' File "%s", line %d\n' % (filename, lineno)) + if badline is not None: + if isinstance(badline, bytes): # python 2 only + badline = badline.decode('utf-8', 'replace') + lines.append(u' %s\n' % badline.strip()) + if offset is not None: + caretspace = badline.rstrip('\n')[:offset].lstrip() + # non-space whitespace (likes tabs) must be kept for alignment + caretspace = ((c.isspace() and c or ' ') for c in caretspace) + # only three spaces to account for offset1 == pos 0 + lines.append(' %s^\n' % ''.join(caretspace)) + value = msg + + lines.append(_format_final_exc_line(stype, value)) + return lines + +def _format_final_exc_line(etype, value): + """Return a list of a single line -- normal case for format_exception_only""" + valuestr = _some_str(value) + if value is None or not valuestr: + line = "%s\n" % etype + else: + line = "%s: %s\n" % (etype, valuestr) + return line + +def _some_str(value): + try: + return unicode(value) + except Exception: + try: + return str(value) + except Exception: + pass + return '' % type(value).__name__ diff --git a/venv/lib/python3.5/site-packages/_pytest/_code/code.py b/venv/lib/python3.5/site-packages/_pytest/_code/code.py new file mode 100644 index 0000000..5b7cc41 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_code/code.py @@ -0,0 +1,895 @@ +from __future__ import absolute_import, division, print_function +import sys +from inspect import CO_VARARGS, CO_VARKEYWORDS +import re +from weakref import ref +from _pytest.compat import _PY2, _PY3, PY35, safe_str + +import py +builtin_repr = repr + +reprlib = py.builtin._tryimport('repr', 'reprlib') + +if _PY3: + from traceback import format_exception_only +else: + from ._py2traceback import format_exception_only + + +class Code(object): + """ wrapper around Python code objects """ + def __init__(self, rawcode): + if not hasattr(rawcode, "co_filename"): + rawcode = getrawcode(rawcode) + try: + self.filename = rawcode.co_filename + self.firstlineno = rawcode.co_firstlineno - 1 + self.name = rawcode.co_name + except AttributeError: + raise TypeError("not a code object: %r" %(rawcode,)) + self.raw = rawcode + + def __eq__(self, other): + return self.raw == other.raw + + __hash__ = None + + def __ne__(self, other): + return not self == other + + @property + def path(self): + """ return a path object pointing to source code (note that it + might not point to an actually existing file). """ + try: + p = py.path.local(self.raw.co_filename) + # maybe don't try this checking + if not p.check(): + raise OSError("py.path check failed.") + except OSError: + # XXX maybe try harder like the weird logic + # in the standard lib [linecache.updatecache] does? + p = self.raw.co_filename + + return p + + @property + def fullsource(self): + """ return a _pytest._code.Source object for the full source file of the code + """ + from _pytest._code import source + full, _ = source.findsource(self.raw) + return full + + def source(self): + """ return a _pytest._code.Source object for the code object's source only + """ + # return source only for that part of code + import _pytest._code + return _pytest._code.Source(self.raw) + + def getargs(self, var=False): + """ return a tuple with the argument names for the code object + + if 'var' is set True also return the names of the variable and + keyword arguments when present + """ + # handfull shortcut for getting args + raw = self.raw + argcount = raw.co_argcount + if var: + argcount += raw.co_flags & CO_VARARGS + argcount += raw.co_flags & CO_VARKEYWORDS + return raw.co_varnames[:argcount] + +class Frame(object): + """Wrapper around a Python frame holding f_locals and f_globals + in which expressions can be evaluated.""" + + def __init__(self, frame): + self.lineno = frame.f_lineno - 1 + self.f_globals = frame.f_globals + self.f_locals = frame.f_locals + self.raw = frame + self.code = Code(frame.f_code) + + @property + def statement(self): + """ statement this frame is at """ + import _pytest._code + if self.code.fullsource is None: + return _pytest._code.Source("") + return self.code.fullsource.getstatement(self.lineno) + + def eval(self, code, **vars): + """ evaluate 'code' in the frame + + 'vars' are optional additional local variables + + returns the result of the evaluation + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + return eval(code, self.f_globals, f_locals) + + def exec_(self, code, **vars): + """ exec 'code' in the frame + + 'vars' are optiona; additional local variables + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + py.builtin.exec_(code, self.f_globals, f_locals ) + + def repr(self, object): + """ return a 'safe' (non-recursive, one-line) string repr for 'object' + """ + return py.io.saferepr(object) + + def is_true(self, object): + return object + + def getargs(self, var=False): + """ return a list of tuples (name, value) for all arguments + + if 'var' is set True also include the variable and keyword + arguments when present + """ + retval = [] + for arg in self.code.getargs(var): + try: + retval.append((arg, self.f_locals[arg])) + except KeyError: + pass # this can occur when using Psyco + return retval + +class TracebackEntry(object): + """ a single entry in a traceback """ + + _repr_style = None + exprinfo = None + + def __init__(self, rawentry, excinfo=None): + self._excinfo = excinfo + self._rawentry = rawentry + self.lineno = rawentry.tb_lineno - 1 + + def set_repr_style(self, mode): + assert mode in ("short", "long") + self._repr_style = mode + + @property + def frame(self): + import _pytest._code + return _pytest._code.Frame(self._rawentry.tb_frame) + + @property + def relline(self): + return self.lineno - self.frame.code.firstlineno + + def __repr__(self): + return "" %(self.frame.code.path, self.lineno+1) + + @property + def statement(self): + """ _pytest._code.Source object for the current statement """ + source = self.frame.code.fullsource + return source.getstatement(self.lineno) + + @property + def path(self): + """ path to the source code """ + return self.frame.code.path + + def getlocals(self): + return self.frame.f_locals + locals = property(getlocals, None, None, "locals of underlaying frame") + + def getfirstlinesource(self): + # on Jython this firstlineno can be -1 apparently + return max(self.frame.code.firstlineno, 0) + + def getsource(self, astcache=None): + """ return failing source code. """ + # we use the passed in astcache to not reparse asttrees + # within exception info printing + from _pytest._code.source import getstatementrange_ast + source = self.frame.code.fullsource + if source is None: + return None + key = astnode = None + if astcache is not None: + key = self.frame.code.path + if key is not None: + astnode = astcache.get(key, None) + start = self.getfirstlinesource() + try: + astnode, _, end = getstatementrange_ast(self.lineno, source, + astnode=astnode) + except SyntaxError: + end = self.lineno + 1 + else: + if key is not None: + astcache[key] = astnode + return source[start:end] + + source = property(getsource) + + def ishidden(self): + """ return True if the current frame has a var __tracebackhide__ + resolving to True + + If __tracebackhide__ is a callable, it gets called with the + ExceptionInfo instance and can decide whether to hide the traceback. + + mostly for internal use + """ + try: + tbh = self.frame.f_locals['__tracebackhide__'] + except KeyError: + try: + tbh = self.frame.f_globals['__tracebackhide__'] + except KeyError: + return False + + if py.builtin.callable(tbh): + return tbh(None if self._excinfo is None else self._excinfo()) + else: + return tbh + + def __str__(self): + try: + fn = str(self.path) + except py.error.Error: + fn = '???' + name = self.frame.code.name + try: + line = str(self.statement).lstrip() + except KeyboardInterrupt: + raise + except: + line = "???" + return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) + + def name(self): + return self.frame.code.raw.co_name + name = property(name, None, None, "co_name of underlaying code") + +class Traceback(list): + """ Traceback objects encapsulate and offer higher level + access to Traceback entries. + """ + Entry = TracebackEntry + def __init__(self, tb, excinfo=None): + """ initialize from given python traceback object and ExceptionInfo """ + self._excinfo = excinfo + if hasattr(tb, 'tb_next'): + def f(cur): + while cur is not None: + yield self.Entry(cur, excinfo=excinfo) + cur = cur.tb_next + list.__init__(self, f(tb)) + else: + list.__init__(self, tb) + + def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None): + """ return a Traceback instance wrapping part of this Traceback + + by provding any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined + + this allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback) + """ + for x in self: + code = x.frame.code + codepath = code.path + if ((path is None or codepath == path) and + (excludepath is None or not hasattr(codepath, 'relto') or + not codepath.relto(excludepath)) and + (lineno is None or x.lineno == lineno) and + (firstlineno is None or x.frame.code.firstlineno == firstlineno)): + return Traceback(x._rawentry, self._excinfo) + return self + + def __getitem__(self, key): + val = super(Traceback, self).__getitem__(key) + if isinstance(key, type(slice(0))): + val = self.__class__(val) + return val + + def filter(self, fn=lambda x: not x.ishidden()): + """ return a Traceback instance with certain items removed + + fn is a function that gets a single argument, a TracebackEntry + instance, and should return True when the item should be added + to the Traceback, False when not + + by default this removes all the TracebackEntries which are hidden + (see ishidden() above) + """ + return Traceback(filter(fn, self), self._excinfo) + + def getcrashentry(self): + """ return last non-hidden traceback entry that lead + to the exception of a traceback. + """ + for i in range(-1, -len(self)-1, -1): + entry = self[i] + if not entry.ishidden(): + return entry + return self[-1] + + def recursionindex(self): + """ return the index of the frame/TracebackEntry where recursion + originates if appropriate, None if no recursion occurred + """ + cache = {} + for i, entry in enumerate(self): + # id for the code.raw is needed to work around + # the strange metaprogramming in the decorator lib from pypi + # which generates code objects that have hash/value equality + #XXX needs a test + key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno + #print "checking for recursion at", key + l = cache.setdefault(key, []) + if l: + f = entry.frame + loc = f.f_locals + for otherloc in l: + if f.is_true(f.eval(co_equal, + __recursioncache_locals_1=loc, + __recursioncache_locals_2=otherloc)): + return i + l.append(entry.frame.f_locals) + return None + + +co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', + '?', 'eval') + +class ExceptionInfo(object): + """ wraps sys.exc_info() objects and offers + help for navigating the traceback. + """ + _striptext = '' + _assert_start_repr = "AssertionError(u\'assert " if _PY2 else "AssertionError(\'assert " + + def __init__(self, tup=None, exprinfo=None): + import _pytest._code + if tup is None: + tup = sys.exc_info() + if exprinfo is None and isinstance(tup[1], AssertionError): + exprinfo = getattr(tup[1], 'msg', None) + if exprinfo is None: + exprinfo = py.io.saferepr(tup[1]) + if exprinfo and exprinfo.startswith(self._assert_start_repr): + self._striptext = 'AssertionError: ' + self._excinfo = tup + #: the exception class + self.type = tup[0] + #: the exception instance + self.value = tup[1] + #: the exception raw traceback + self.tb = tup[2] + #: the exception type name + self.typename = self.type.__name__ + #: the exception traceback (_pytest._code.Traceback instance) + self.traceback = _pytest._code.Traceback(self.tb, excinfo=ref(self)) + + def __repr__(self): + return "" % (self.typename, len(self.traceback)) + + def exconly(self, tryshort=False): + """ return the exception as a string + + when 'tryshort' resolves to True, and the exception is a + _pytest._code._AssertionError, only the actual exception part of + the exception representation is returned (so 'AssertionError: ' is + removed from the beginning) + """ + lines = format_exception_only(self.type, self.value) + text = ''.join(lines) + text = text.rstrip() + if tryshort: + if text.startswith(self._striptext): + text = text[len(self._striptext):] + return text + + def errisinstance(self, exc): + """ return True if the exception is an instance of exc """ + return isinstance(self.value, exc) + + def _getreprcrash(self): + exconly = self.exconly(tryshort=True) + entry = self.traceback.getcrashentry() + path, lineno = entry.frame.code.raw.co_filename, entry.lineno + return ReprFileLocation(path, lineno+1, exconly) + + def getrepr(self, showlocals=False, style="long", + abspath=False, tbfilter=True, funcargs=False): + """ return str()able representation of this exception info. + showlocals: show locals per traceback entry + style: long|short|no|native traceback style + tbfilter: hide entries (where __tracebackhide__ is true) + + in case of style==native, tbfilter and showlocals is ignored. + """ + if style == 'native': + return ReprExceptionInfo(ReprTracebackNative( + py.std.traceback.format_exception( + self.type, + self.value, + self.traceback[0]._rawentry, + )), self._getreprcrash()) + + fmt = FormattedExcinfo(showlocals=showlocals, style=style, + abspath=abspath, tbfilter=tbfilter, funcargs=funcargs) + return fmt.repr_excinfo(self) + + def __str__(self): + entry = self.traceback[-1] + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return str(loc) + + def __unicode__(self): + entry = self.traceback[-1] + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return unicode(loc) + + def match(self, regexp): + """ + Match the regular expression 'regexp' on the string representation of + the exception. If it matches then True is returned (so that it is + possible to write 'assert excinfo.match()'). If it doesn't match an + AssertionError is raised. + """ + __tracebackhide__ = True + if not re.search(regexp, str(self.value)): + assert 0, "Pattern '{0!s}' not found in '{1!s}'".format( + regexp, self.value) + return True + + +class FormattedExcinfo(object): + """ presenting information about failing Functions and Generators. """ + # for traceback entries + flow_marker = ">" + fail_marker = "E" + + def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False): + self.showlocals = showlocals + self.style = style + self.tbfilter = tbfilter + self.funcargs = funcargs + self.abspath = abspath + self.astcache = {} + + def _getindent(self, source): + # figure out indent for given source + try: + s = str(source.getstatement(len(source)-1)) + except KeyboardInterrupt: + raise + except: + try: + s = str(source[-1]) + except KeyboardInterrupt: + raise + except: + return 0 + return 4 + (len(s) - len(s.lstrip())) + + def _getentrysource(self, entry): + source = entry.getsource(self.astcache) + if source is not None: + source = source.deindent() + return source + + def _saferepr(self, obj): + return py.io.saferepr(obj) + + def repr_args(self, entry): + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(var=True): + args.append((argname, self._saferepr(argvalue))) + return ReprFuncArgs(args) + + def get_source(self, source, line_index=-1, excinfo=None, short=False): + """ return formatted and marked up source lines. """ + import _pytest._code + lines = [] + if source is None or line_index >= len(source.lines): + source = _pytest._code.Source("???") + line_index = 0 + if line_index < 0: + line_index += len(source) + space_prefix = " " + if short: + lines.append(space_prefix + source.lines[line_index].strip()) + else: + for line in source.lines[:line_index]: + lines.append(space_prefix + line) + lines.append(self.flow_marker + " " + source.lines[line_index]) + for line in source.lines[line_index+1:]: + lines.append(space_prefix + line) + if excinfo is not None: + indent = 4 if short else self._getindent(source) + lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) + return lines + + def get_exconly(self, excinfo, indent=4, markall=False): + lines = [] + indent = " " * indent + # get the real exception information out + exlines = excinfo.exconly(tryshort=True).split('\n') + failindent = self.fail_marker + indent[1:] + for line in exlines: + lines.append(failindent + line) + if not markall: + failindent = indent + return lines + + def repr_locals(self, locals): + if self.showlocals: + lines = [] + keys = [loc for loc in locals if loc[0] != "@"] + keys.sort() + for name in keys: + value = locals[name] + if name == '__builtins__': + lines.append("__builtins__ = ") + else: + # This formatting could all be handled by the + # _repr() function, which is only reprlib.Repr in + # disguise, so is very configurable. + str_repr = self._saferepr(value) + #if len(str_repr) < 70 or not isinstance(value, + # (list, tuple, dict)): + lines.append("%-10s = %s" %(name, str_repr)) + #else: + # self._line("%-10s =\\" % (name,)) + # # XXX + # py.std.pprint.pprint(value, stream=self.excinfowriter) + return ReprLocals(lines) + + def repr_traceback_entry(self, entry, excinfo=None): + import _pytest._code + source = self._getentrysource(entry) + if source is None: + source = _pytest._code.Source("???") + line_index = 0 + else: + # entry.getfirstlinesource() can be -1, should be 0 on jython + line_index = entry.lineno - max(entry.getfirstlinesource(), 0) + + lines = [] + style = entry._repr_style + if style is None: + style = self.style + if style in ("short", "long"): + short = style == "short" + reprargs = self.repr_args(entry) if not short else None + s = self.get_source(source, line_index, excinfo, short=short) + lines.extend(s) + if short: + message = "in %s" %(entry.name) + else: + message = excinfo and excinfo.typename or "" + path = self._makepath(entry.path) + filelocrepr = ReprFileLocation(path, entry.lineno+1, message) + localsrepr = None + if not short: + localsrepr = self.repr_locals(entry.locals) + return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style) + if excinfo: + lines.extend(self.get_exconly(excinfo, indent=4)) + return ReprEntry(lines, None, None, None, style) + + def _makepath(self, path): + if not self.abspath: + try: + np = py.path.local().bestrelpath(path) + except OSError: + return path + if len(np) < len(str(path)): + path = np + return path + + def repr_traceback(self, excinfo): + traceback = excinfo.traceback + if self.tbfilter: + traceback = traceback.filter() + + if is_recursion_error(excinfo): + traceback, extraline = self._truncate_recursive_traceback(traceback) + else: + extraline = None + + last = traceback[-1] + entries = [] + for index, entry in enumerate(traceback): + einfo = (last == entry) and excinfo or None + reprentry = self.repr_traceback_entry(entry, einfo) + entries.append(reprentry) + return ReprTraceback(entries, extraline, style=self.style) + + def _truncate_recursive_traceback(self, traceback): + """ + Truncate the given recursive traceback trying to find the starting point + of the recursion. + + The detection is done by going through each traceback entry and finding the + point in which the locals of the frame are equal to the locals of a previous frame (see ``recursionindex()``. + + Handle the situation where the recursion process might raise an exception (for example + comparing numpy arrays using equality raises a TypeError), in which case we do our best to + warn the user of the error and show a limited traceback. + """ + try: + recursionindex = traceback.recursionindex() + except Exception as e: + max_frames = 10 + extraline = ( + '!!! Recursion error detected, but an error occurred locating the origin of recursion.\n' + ' The following exception happened when comparing locals in the stack frame:\n' + ' {exc_type}: {exc_msg}\n' + ' Displaying first and last {max_frames} stack frames out of {total}.' + ).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback)) + traceback = traceback[:max_frames] + traceback[-max_frames:] + else: + if recursionindex is not None: + extraline = "!!! Recursion detected (same locals & position)" + traceback = traceback[:recursionindex + 1] + else: + extraline = None + + return traceback, extraline + + def repr_excinfo(self, excinfo): + if _PY2: + reprtraceback = self.repr_traceback(excinfo) + reprcrash = excinfo._getreprcrash() + + return ReprExceptionInfo(reprtraceback, reprcrash) + else: + repr_chain = [] + e = excinfo.value + descr = None + while e is not None: + if excinfo: + reprtraceback = self.repr_traceback(excinfo) + reprcrash = excinfo._getreprcrash() + else: + # fallback to native repr if the exception doesn't have a traceback: + # ExceptionInfo objects require a full traceback to work + reprtraceback = ReprTracebackNative(py.std.traceback.format_exception(type(e), e, None)) + reprcrash = None + + repr_chain += [(reprtraceback, reprcrash, descr)] + if e.__cause__ is not None: + e = e.__cause__ + excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None + descr = 'The above exception was the direct cause of the following exception:' + elif e.__context__ is not None: + e = e.__context__ + excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None + descr = 'During handling of the above exception, another exception occurred:' + else: + e = None + repr_chain.reverse() + return ExceptionChainRepr(repr_chain) + + +class TerminalRepr(object): + def __str__(self): + s = self.__unicode__() + if _PY2: + s = s.encode('utf-8') + return s + + def __unicode__(self): + # FYI this is called from pytest-xdist's serialization of exception + # information. + io = py.io.TextIO() + tw = py.io.TerminalWriter(file=io) + self.toterminal(tw) + return io.getvalue().strip() + + def __repr__(self): + return "<%s instance at %0x>" %(self.__class__, id(self)) + + +class ExceptionRepr(TerminalRepr): + def __init__(self): + self.sections = [] + + def addsection(self, name, content, sep="-"): + self.sections.append((name, content, sep)) + + def toterminal(self, tw): + for name, content, sep in self.sections: + tw.sep(sep, name) + tw.line(content) + + +class ExceptionChainRepr(ExceptionRepr): + def __init__(self, chain): + super(ExceptionChainRepr, self).__init__() + self.chain = chain + # reprcrash and reprtraceback of the outermost (the newest) exception + # in the chain + self.reprtraceback = chain[-1][0] + self.reprcrash = chain[-1][1] + + def toterminal(self, tw): + for element in self.chain: + element[0].toterminal(tw) + if element[2] is not None: + tw.line("") + tw.line(element[2], yellow=True) + super(ExceptionChainRepr, self).toterminal(tw) + + +class ReprExceptionInfo(ExceptionRepr): + def __init__(self, reprtraceback, reprcrash): + super(ReprExceptionInfo, self).__init__() + self.reprtraceback = reprtraceback + self.reprcrash = reprcrash + + def toterminal(self, tw): + self.reprtraceback.toterminal(tw) + super(ReprExceptionInfo, self).toterminal(tw) + +class ReprTraceback(TerminalRepr): + entrysep = "_ " + + def __init__(self, reprentries, extraline, style): + self.reprentries = reprentries + self.extraline = extraline + self.style = style + + def toterminal(self, tw): + # the entries might have different styles + for i, entry in enumerate(self.reprentries): + if entry.style == "long": + tw.line("") + entry.toterminal(tw) + if i < len(self.reprentries) - 1: + next_entry = self.reprentries[i+1] + if entry.style == "long" or \ + entry.style == "short" and next_entry.style == "long": + tw.sep(self.entrysep) + + if self.extraline: + tw.line(self.extraline) + +class ReprTracebackNative(ReprTraceback): + def __init__(self, tblines): + self.style = "native" + self.reprentries = [ReprEntryNative(tblines)] + self.extraline = None + +class ReprEntryNative(TerminalRepr): + style = "native" + + def __init__(self, tblines): + self.lines = tblines + + def toterminal(self, tw): + tw.write("".join(self.lines)) + +class ReprEntry(TerminalRepr): + localssep = "_ " + + def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style): + self.lines = lines + self.reprfuncargs = reprfuncargs + self.reprlocals = reprlocals + self.reprfileloc = filelocrepr + self.style = style + + def toterminal(self, tw): + if self.style == "short": + self.reprfileloc.toterminal(tw) + for line in self.lines: + red = line.startswith("E ") + tw.line(line, bold=True, red=red) + #tw.line("") + return + if self.reprfuncargs: + self.reprfuncargs.toterminal(tw) + for line in self.lines: + red = line.startswith("E ") + tw.line(line, bold=True, red=red) + if self.reprlocals: + #tw.sep(self.localssep, "Locals") + tw.line("") + self.reprlocals.toterminal(tw) + if self.reprfileloc: + if self.lines: + tw.line("") + self.reprfileloc.toterminal(tw) + + def __str__(self): + return "%s\n%s\n%s" % ("\n".join(self.lines), + self.reprlocals, + self.reprfileloc) + +class ReprFileLocation(TerminalRepr): + def __init__(self, path, lineno, message): + self.path = str(path) + self.lineno = lineno + self.message = message + + def toterminal(self, tw): + # filename and lineno output for each entry, + # using an output format that most editors unterstand + msg = self.message + i = msg.find("\n") + if i != -1: + msg = msg[:i] + tw.write(self.path, bold=True, red=True) + tw.line(":%s: %s" % (self.lineno, msg)) + +class ReprLocals(TerminalRepr): + def __init__(self, lines): + self.lines = lines + + def toterminal(self, tw): + for line in self.lines: + tw.line(line) + +class ReprFuncArgs(TerminalRepr): + def __init__(self, args): + self.args = args + + def toterminal(self, tw): + if self.args: + linesofar = "" + for name, value in self.args: + ns = "%s = %s" %(name, value) + if len(ns) + len(linesofar) + 2 > tw.fullwidth: + if linesofar: + tw.line(linesofar) + linesofar = ns + else: + if linesofar: + linesofar += ", " + ns + else: + linesofar = ns + if linesofar: + tw.line(linesofar) + tw.line("") + + +def getrawcode(obj, trycall=True): + """ return code object for given function. """ + try: + return obj.__code__ + except AttributeError: + obj = getattr(obj, 'im_func', obj) + obj = getattr(obj, 'func_code', obj) + obj = getattr(obj, 'f_code', obj) + obj = getattr(obj, '__code__', obj) + if trycall and not hasattr(obj, 'co_firstlineno'): + if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj): + x = getrawcode(obj.__call__, trycall=False) + if hasattr(x, 'co_firstlineno'): + return x + return obj + + +if PY35: # RecursionError introduced in 3.5 + def is_recursion_error(excinfo): + return excinfo.errisinstance(RecursionError) # noqa +else: + def is_recursion_error(excinfo): + if not excinfo.errisinstance(RuntimeError): + return False + try: + return "maximum recursion depth exceeded" in str(excinfo.value) + except UnicodeError: + return False diff --git a/venv/lib/python3.5/site-packages/_pytest/_code/source.py b/venv/lib/python3.5/site-packages/_pytest/_code/source.py new file mode 100644 index 0000000..8e61484 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_code/source.py @@ -0,0 +1,414 @@ +from __future__ import absolute_import, division, generators, print_function + +from bisect import bisect_right +import sys +import inspect, tokenize +import py +cpy_compile = compile + +try: + import _ast + from _ast import PyCF_ONLY_AST as _AST_FLAG +except ImportError: + _AST_FLAG = 0 + _ast = None + + +class Source(object): + """ a immutable object holding a source code fragment, + possibly deindenting it. + """ + _compilecounter = 0 + def __init__(self, *parts, **kwargs): + self.lines = lines = [] + de = kwargs.get('deindent', True) + rstrip = kwargs.get('rstrip', True) + for part in parts: + if not part: + partlines = [] + if isinstance(part, Source): + partlines = part.lines + elif isinstance(part, (tuple, list)): + partlines = [x.rstrip("\n") for x in part] + elif isinstance(part, py.builtin._basestring): + partlines = part.split('\n') + if rstrip: + while partlines: + if partlines[-1].strip(): + break + partlines.pop() + else: + partlines = getsource(part, deindent=de).lines + if de: + partlines = deindent(partlines) + lines.extend(partlines) + + def __eq__(self, other): + try: + return self.lines == other.lines + except AttributeError: + if isinstance(other, str): + return str(self) == other + return False + + __hash__ = None + + def __getitem__(self, key): + if isinstance(key, int): + return self.lines[key] + else: + if key.step not in (None, 1): + raise IndexError("cannot slice a Source with a step") + newsource = Source() + newsource.lines = self.lines[key.start:key.stop] + return newsource + + def __len__(self): + return len(self.lines) + + def strip(self): + """ return new source object with trailing + and leading blank lines removed. + """ + start, end = 0, len(self) + while start < end and not self.lines[start].strip(): + start += 1 + while end > start and not self.lines[end-1].strip(): + end -= 1 + source = Source() + source.lines[:] = self.lines[start:end] + return source + + def putaround(self, before='', after='', indent=' ' * 4): + """ return a copy of the source object with + 'before' and 'after' wrapped around it. + """ + before = Source(before) + after = Source(after) + newsource = Source() + lines = [ (indent + line) for line in self.lines] + newsource.lines = before.lines + lines + after.lines + return newsource + + def indent(self, indent=' ' * 4): + """ return a copy of the source object with + all lines indented by the given indent-string. + """ + newsource = Source() + newsource.lines = [(indent+line) for line in self.lines] + return newsource + + def getstatement(self, lineno, assertion=False): + """ return Source statement which contains the + given linenumber (counted from 0). + """ + start, end = self.getstatementrange(lineno, assertion) + return self[start:end] + + def getstatementrange(self, lineno, assertion=False): + """ return (start, end) tuple which spans the minimal + statement region which containing the given lineno. + """ + if not (0 <= lineno < len(self)): + raise IndexError("lineno out of range") + ast, start, end = getstatementrange_ast(lineno, self) + return start, end + + def deindent(self, offset=None): + """ return a new source object deindented by offset. + If offset is None then guess an indentation offset from + the first non-blank line. Subsequent lines which have a + lower indentation offset will be copied verbatim as + they are assumed to be part of multilines. + """ + # XXX maybe use the tokenizer to properly handle multiline + # strings etc.pp? + newsource = Source() + newsource.lines[:] = deindent(self.lines, offset) + return newsource + + def isparseable(self, deindent=True): + """ return True if source is parseable, heuristically + deindenting it by default. + """ + try: + import parser + except ImportError: + syntax_checker = lambda x: compile(x, 'asd', 'exec') + else: + syntax_checker = parser.suite + + if deindent: + source = str(self.deindent()) + else: + source = str(self) + try: + #compile(source+'\n', "x", "exec") + syntax_checker(source+'\n') + except KeyboardInterrupt: + raise + except Exception: + return False + else: + return True + + def __str__(self): + return "\n".join(self.lines) + + def compile(self, filename=None, mode='exec', + flag=generators.compiler_flag, + dont_inherit=0, _genframe=None): + """ return compiled code object. if filename is None + invent an artificial filename which displays + the source/line position of the caller frame. + """ + if not filename or py.path.local(filename).check(file=0): + if _genframe is None: + _genframe = sys._getframe(1) # the caller + fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno + base = "<%d-codegen " % self._compilecounter + self.__class__._compilecounter += 1 + if not filename: + filename = base + '%s:%d>' % (fn, lineno) + else: + filename = base + '%r %s:%d>' % (filename, fn, lineno) + source = "\n".join(self.lines) + '\n' + try: + co = cpy_compile(source, filename, mode, flag) + except SyntaxError: + ex = sys.exc_info()[1] + # re-represent syntax errors from parsing python strings + msglines = self.lines[:ex.lineno] + if ex.offset: + msglines.append(" "*ex.offset + '^') + msglines.append("(code was compiled probably from here: %s)" % filename) + newex = SyntaxError('\n'.join(msglines)) + newex.offset = ex.offset + newex.lineno = ex.lineno + newex.text = ex.text + raise newex + else: + if flag & _AST_FLAG: + return co + lines = [(x + "\n") for x in self.lines] + py.std.linecache.cache[filename] = (1, None, lines, filename) + return co + +# +# public API shortcut functions +# + +def compile_(source, filename=None, mode='exec', flags= + generators.compiler_flag, dont_inherit=0): + """ compile the given source to a raw code object, + and maintain an internal cache which allows later + retrieval of the source code for the code object + and any recursively created code objects. + """ + if _ast is not None and isinstance(source, _ast.AST): + # XXX should Source support having AST? + return cpy_compile(source, filename, mode, flags, dont_inherit) + _genframe = sys._getframe(1) # the caller + s = Source(source) + co = s.compile(filename, mode, flags, _genframe=_genframe) + return co + + +def getfslineno(obj): + """ Return source location (path, lineno) for the given object. + If the source cannot be determined return ("", -1) + """ + import _pytest._code + try: + code = _pytest._code.Code(obj) + except TypeError: + try: + fn = (py.std.inspect.getsourcefile(obj) or + py.std.inspect.getfile(obj)) + except TypeError: + return "", -1 + + fspath = fn and py.path.local(fn) or None + lineno = -1 + if fspath: + try: + _, lineno = findsource(obj) + except IOError: + pass + else: + fspath = code.path + lineno = code.firstlineno + assert isinstance(lineno, int) + return fspath, lineno + +# +# helper functions +# + +def findsource(obj): + try: + sourcelines, lineno = py.std.inspect.findsource(obj) + except py.builtin._sysex: + raise + except: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in sourcelines] + return source, lineno + + +def getsource(obj, **kwargs): + import _pytest._code + obj = _pytest._code.getrawcode(obj) + try: + strsrc = inspect.getsource(obj) + except IndentationError: + strsrc = "\"Buggy python version consider upgrading, cannot get source\"" + assert isinstance(strsrc, str) + return Source(strsrc, **kwargs) + + +def deindent(lines, offset=None): + if offset is None: + for line in lines: + line = line.expandtabs() + s = line.lstrip() + if s: + offset = len(line)-len(s) + break + else: + offset = 0 + if offset == 0: + return list(lines) + newlines = [] + + def readline_generator(lines): + for line in lines: + yield line + '\n' + while True: + yield '' + + it = readline_generator(lines) + + try: + for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)): + if sline > len(lines): + break # End of input reached + if sline > len(newlines): + line = lines[sline - 1].expandtabs() + if line.lstrip() and line[:offset].isspace(): + line = line[offset:] # Deindent + newlines.append(line) + + for i in range(sline, eline): + # Don't deindent continuing lines of + # multiline tokens (i.e. multiline strings) + newlines.append(lines[i]) + except (IndentationError, tokenize.TokenError): + pass + # Add any lines we didn't see. E.g. if an exception was raised. + newlines.extend(lines[len(newlines):]) + return newlines + + +def get_statement_startend2(lineno, node): + import ast + # flatten all statements and except handlers into one lineno-list + # AST's line numbers start indexing at 1 + l = [] + for x in ast.walk(node): + if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler): + l.append(x.lineno - 1) + for name in "finalbody", "orelse": + val = getattr(x, name, None) + if val: + # treat the finally/orelse part as its own statement + l.append(val[0].lineno - 1 - 1) + l.sort() + insert_index = bisect_right(l, lineno) + start = l[insert_index - 1] + if insert_index >= len(l): + end = None + else: + end = l[insert_index] + return start, end + + +def getstatementrange_ast(lineno, source, assertion=False, astnode=None): + if astnode is None: + content = str(source) + if sys.version_info < (2,7): + content += "\n" + try: + astnode = compile(content, "source", "exec", 1024) # 1024 for AST + except ValueError: + start, end = getstatementrange_old(lineno, source, assertion) + return None, start, end + start, end = get_statement_startend2(lineno, astnode) + # we need to correct the end: + # - ast-parsing strips comments + # - there might be empty lines + # - we might have lesser indented code blocks at the end + if end is None: + end = len(source.lines) + + if end > start + 1: + # make sure we don't span differently indented code blocks + # by using the BlockFinder helper used which inspect.getsource() uses itself + block_finder = inspect.BlockFinder() + # if we start with an indented line, put blockfinder to "started" mode + block_finder.started = source.lines[start][0].isspace() + it = ((x + "\n") for x in source.lines[start:end]) + try: + for tok in tokenize.generate_tokens(lambda: next(it)): + block_finder.tokeneater(*tok) + except (inspect.EndOfBlock, IndentationError): + end = block_finder.last + start + except Exception: + pass + + # the end might still point to a comment or empty line, correct it + while end: + line = source.lines[end - 1].lstrip() + if line.startswith("#") or not line: + end -= 1 + else: + break + return astnode, start, end + + +def getstatementrange_old(lineno, source, assertion=False): + """ return (start, end) tuple which spans the minimal + statement region which containing the given lineno. + raise an IndexError if no such statementrange can be found. + """ + # XXX this logic is only used on python2.4 and below + # 1. find the start of the statement + from codeop import compile_command + for start in range(lineno, -1, -1): + if assertion: + line = source.lines[start] + # the following lines are not fully tested, change with care + if 'super' in line and 'self' in line and '__init__' in line: + raise IndexError("likely a subclass") + if "assert" not in line and "raise" not in line: + continue + trylines = source.lines[start:lineno+1] + # quick hack to prepare parsing an indented line with + # compile_command() (which errors on "return" outside defs) + trylines.insert(0, 'def xxx():') + trysource = '\n '.join(trylines) + # ^ space here + try: + compile_command(trysource) + except (SyntaxError, OverflowError, ValueError): + continue + + # 2. find the end of the statement + for end in range(lineno+1, len(source)+1): + trysource = source[start:end] + if trysource.isparseable(): + return start, end + raise SyntaxError("no valid source range around line %d " % (lineno,)) + + diff --git a/venv/lib/python3.5/site-packages/_pytest/_pluggy.py b/venv/lib/python3.5/site-packages/_pytest/_pluggy.py new file mode 100644 index 0000000..6cc1d3d --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_pluggy.py @@ -0,0 +1,11 @@ +""" +imports symbols from vendored "pluggy" if available, otherwise +falls back to importing "pluggy" from the default namespace. +""" +from __future__ import absolute_import, division, print_function +try: + from _pytest.vendored_packages.pluggy import * # noqa + from _pytest.vendored_packages.pluggy import __version__ # noqa +except ImportError: + from pluggy import * # noqa + from pluggy import __version__ # noqa diff --git a/venv/lib/python3.5/site-packages/_pytest/_version.py b/venv/lib/python3.5/site-packages/_pytest/_version.py new file mode 100644 index 0000000..e03109e --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/_version.py @@ -0,0 +1,4 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '3.1.3' diff --git a/venv/lib/python3.5/site-packages/_pytest/assertion/__init__.py b/venv/lib/python3.5/site-packages/_pytest/assertion/__init__.py new file mode 100644 index 0000000..acb034d --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/assertion/__init__.py @@ -0,0 +1,149 @@ +""" +support for presenting detailed information in failing assertions. +""" +from __future__ import absolute_import, division, print_function +import py +import sys + +from _pytest.assertion import util +from _pytest.assertion import rewrite +from _pytest.assertion import truncate + + +def pytest_addoption(parser): + group = parser.getgroup("debugconfig") + group.addoption('--assert', + action="store", + dest="assertmode", + choices=("rewrite", "plain",), + default="rewrite", + metavar="MODE", + help="""Control assertion debugging tools. 'plain' + performs no assertion debugging. 'rewrite' + (the default) rewrites assert statements in + test modules on import to provide assert + expression information.""") + + + +def register_assert_rewrite(*names): + """Register one or more module names to be rewritten on import. + + This function will make sure that this module or all modules inside + the package will get their assert statements rewritten. + Thus you should make sure to call this before the module is + actually imported, usually in your __init__.py if you are a plugin + using a package. + + :raise TypeError: if the given module names are not strings. + """ + for name in names: + if not isinstance(name, str): + msg = 'expected module names as *args, got {0} instead' + raise TypeError(msg.format(repr(names))) + for hook in sys.meta_path: + if isinstance(hook, rewrite.AssertionRewritingHook): + importhook = hook + break + else: + importhook = DummyRewriteHook() + importhook.mark_rewrite(*names) + + +class DummyRewriteHook(object): + """A no-op import hook for when rewriting is disabled.""" + + def mark_rewrite(self, *names): + pass + + +class AssertionState: + """State for the assertion plugin.""" + + def __init__(self, config, mode): + self.mode = mode + self.trace = config.trace.root.get("assertion") + self.hook = None + + +def install_importhook(config): + """Try to install the rewrite hook, raise SystemError if it fails.""" + # Both Jython and CPython 2.6.0 have AST bugs that make the + # assertion rewriting hook malfunction. + if (sys.platform.startswith('java') or + sys.version_info[:3] == (2, 6, 0)): + raise SystemError('rewrite not supported') + + config._assertstate = AssertionState(config, 'rewrite') + config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config._assertstate.trace('installed rewrite import hook') + + def undo(): + hook = config._assertstate.hook + if hook is not None and hook in sys.meta_path: + sys.meta_path.remove(hook) + + config.add_cleanup(undo) + return hook + + +def pytest_collection(session): + # this hook is only called when test modules are collected + # so for example not in the master process of pytest-xdist + # (which does not collect test modules) + assertstate = getattr(session.config, '_assertstate', None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(session) + + +def pytest_runtest_setup(item): + """Setup the pytest_assertrepr_compare hook + + The newinterpret and rewrite modules will use util._reprcompare if + it exists to use custom reporting via the + pytest_assertrepr_compare hook. This sets up this custom + comparison for the test. + """ + def callbinrepr(op, left, right): + """Call the pytest_assertrepr_compare hook and prepare the result + + This uses the first result from the hook and then ensures the + following: + * Overly verbose explanations are truncated unless configured otherwise + (eg. if running in verbose mode). + * Embedded newlines are escaped to help util.format_explanation() + later. + * If the rewrite mode is used embedded %-characters are replaced + to protect later % formatting. + + The result can be formatted by util.format_explanation() for + pretty printing. + """ + hook_result = item.ihook.pytest_assertrepr_compare( + config=item.config, op=op, left=left, right=right) + for new_expl in hook_result: + if new_expl: + new_expl = truncate.truncate_if_required(new_expl, item) + new_expl = [line.replace("\n", "\\n") for line in new_expl] + res = py.builtin._totext("\n~").join(new_expl) + if item.config.getvalue("assertmode") == "rewrite": + res = res.replace("%", "%%") + return res + util._reprcompare = callbinrepr + + +def pytest_runtest_teardown(item): + util._reprcompare = None + + +def pytest_sessionfinish(session): + assertstate = getattr(session.config, '_assertstate', None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(None) + + +# Expose this plugin's implementation for the pytest_assertrepr_compare hook +pytest_assertrepr_compare = util.assertrepr_compare diff --git a/venv/lib/python3.5/site-packages/_pytest/assertion/rewrite.py b/venv/lib/python3.5/site-packages/_pytest/assertion/rewrite.py new file mode 100644 index 0000000..6ec54d7 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/assertion/rewrite.py @@ -0,0 +1,941 @@ +"""Rewrite assertion AST to produce nice error messages""" +from __future__ import absolute_import, division, print_function +import ast +import _ast +import errno +import itertools +import imp +import marshal +import os +import re +import struct +import sys +import types + +import py +from _pytest.assertion import util + + +# pytest caches rewritten pycs in __pycache__. +if hasattr(imp, "get_tag"): + PYTEST_TAG = imp.get_tag() + "-PYTEST" +else: + if hasattr(sys, "pypy_version_info"): + impl = "pypy" + elif sys.platform == "java": + impl = "jython" + else: + impl = "cpython" + ver = sys.version_info + PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1]) + del ver, impl + +PYC_EXT = ".py" + (__debug__ and "c" or "o") +PYC_TAIL = "." + PYTEST_TAG + PYC_EXT + +REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2) +ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3 + +if sys.version_info >= (3,5): + ast_Call = ast.Call +else: + ast_Call = lambda a,b,c: ast.Call(a, b, c, None, None) + + +class AssertionRewritingHook(object): + """PEP302 Import hook which rewrites asserts.""" + + def __init__(self, config): + self.config = config + self.fnpats = config.getini("python_files") + self.session = None + self.modules = {} + self._rewritten_names = set() + self._register_with_pkg_resources() + self._must_rewrite = set() + + def set_session(self, session): + self.session = session + + def find_module(self, name, path=None): + state = self.config._assertstate + state.trace("find_module called for: %s" % name) + names = name.rsplit(".", 1) + lastname = names[-1] + pth = None + if path is not None: + # Starting with Python 3.3, path is a _NamespacePath(), which + # causes problems if not converted to list. + path = list(path) + if len(path) == 1: + pth = path[0] + if pth is None: + try: + fd, fn, desc = imp.find_module(lastname, path) + except ImportError: + return None + if fd is not None: + fd.close() + tp = desc[2] + if tp == imp.PY_COMPILED: + if hasattr(imp, "source_from_cache"): + try: + fn = imp.source_from_cache(fn) + except ValueError: + # Python 3 doesn't like orphaned but still-importable + # .pyc files. + fn = fn[:-1] + else: + fn = fn[:-1] + elif tp != imp.PY_SOURCE: + # Don't know what this is. + return None + else: + fn = os.path.join(pth, name.rpartition(".")[2] + ".py") + + fn_pypath = py.path.local(fn) + if not self._should_rewrite(name, fn_pypath, state): + return None + + self._rewritten_names.add(name) + + # The requested module looks like a test file, so rewrite it. This is + # the most magical part of the process: load the source, rewrite the + # asserts, and load the rewritten source. We also cache the rewritten + # module code in a special pyc. We must be aware of the possibility of + # concurrent pytest processes rewriting and loading pycs. To avoid + # tricky race conditions, we maintain the following invariant: The + # cached pyc is always a complete, valid pyc. Operations on it must be + # atomic. POSIX's atomic rename comes in handy. + write = not sys.dont_write_bytecode + cache_dir = os.path.join(fn_pypath.dirname, "__pycache__") + if write: + try: + os.mkdir(cache_dir) + except OSError: + e = sys.exc_info()[1].errno + if e == errno.EEXIST: + # Either the __pycache__ directory already exists (the + # common case) or it's blocked by a non-dir node. In the + # latter case, we'll ignore it in _write_pyc. + pass + elif e in [errno.ENOENT, errno.ENOTDIR]: + # One of the path components was not a directory, likely + # because we're in a zip file. + write = False + elif e in [errno.EACCES, errno.EROFS, errno.EPERM]: + state.trace("read only directory: %r" % fn_pypath.dirname) + write = False + else: + raise + cache_name = fn_pypath.basename[:-3] + PYC_TAIL + pyc = os.path.join(cache_dir, cache_name) + # Notice that even if we're in a read-only directory, I'm going + # to check for a cached pyc. This may not be optimal... + co = _read_pyc(fn_pypath, pyc, state.trace) + if co is None: + state.trace("rewriting %r" % (fn,)) + source_stat, co = _rewrite_test(self.config, fn_pypath) + if co is None: + # Probably a SyntaxError in the test. + return None + if write: + _make_rewritten_pyc(state, source_stat, pyc, co) + else: + state.trace("found cached rewritten pyc for %r" % (fn,)) + self.modules[name] = co, pyc + return self + + def _should_rewrite(self, name, fn_pypath, state): + # always rewrite conftest files + fn = str(fn_pypath) + if fn_pypath.basename == 'conftest.py': + state.trace("rewriting conftest file: %r" % (fn,)) + return True + + if self.session is not None: + if self.session.isinitpath(fn): + state.trace("matched test file (was specified on cmdline): %r" % + (fn,)) + return True + + # modules not passed explicitly on the command line are only + # rewritten if they match the naming convention for test files + for pat in self.fnpats: + if fn_pypath.fnmatch(pat): + state.trace("matched test file %r" % (fn,)) + return True + + for marked in self._must_rewrite: + if name.startswith(marked): + state.trace("matched marked file %r (from %r)" % (name, marked)) + return True + + return False + + def mark_rewrite(self, *names): + """Mark import names as needing to be re-written. + + The named module or package as well as any nested modules will + be re-written on import. + """ + already_imported = set(names).intersection(set(sys.modules)) + if already_imported: + for name in already_imported: + if name not in self._rewritten_names: + self._warn_already_imported(name) + self._must_rewrite.update(names) + + def _warn_already_imported(self, name): + self.config.warn( + 'P1', + 'Module already imported so can not be re-written: %s' % name) + + def load_module(self, name): + # If there is an existing module object named 'fullname' in + # sys.modules, the loader must use that existing module. (Otherwise, + # the reload() builtin will not work correctly.) + if name in sys.modules: + return sys.modules[name] + + co, pyc = self.modules.pop(name) + # I wish I could just call imp.load_compiled here, but __file__ has to + # be set properly. In Python 3.2+, this all would be handled correctly + # by load_compiled. + mod = sys.modules[name] = imp.new_module(name) + try: + mod.__file__ = co.co_filename + # Normally, this attribute is 3.2+. + mod.__cached__ = pyc + mod.__loader__ = self + py.builtin.exec_(co, mod.__dict__) + except: + if name in sys.modules: + del sys.modules[name] + raise + return sys.modules[name] + + + + def is_package(self, name): + try: + fd, fn, desc = imp.find_module(name) + except ImportError: + return False + if fd is not None: + fd.close() + tp = desc[2] + return tp == imp.PKG_DIRECTORY + + @classmethod + def _register_with_pkg_resources(cls): + """ + Ensure package resources can be loaded from this loader. May be called + multiple times, as the operation is idempotent. + """ + try: + import pkg_resources + # access an attribute in case a deferred importer is present + pkg_resources.__name__ + except ImportError: + return + + # Since pytest tests are always located in the file system, the + # DefaultProvider is appropriate. + pkg_resources.register_loader_type(cls, pkg_resources.DefaultProvider) + + def get_data(self, pathname): + """Optional PEP302 get_data API. + """ + with open(pathname, 'rb') as f: + return f.read() + + +def _write_pyc(state, co, source_stat, pyc): + # Technically, we don't have to have the same pyc format as + # (C)Python, since these "pycs" should never be seen by builtin + # import. However, there's little reason deviate, and I hope + # sometime to be able to use imp.load_compiled to load them. (See + # the comment in load_module above.) + try: + fp = open(pyc, "wb") + except IOError: + err = sys.exc_info()[1].errno + state.trace("error writing pyc file at %s: errno=%s" %(pyc, err)) + # we ignore any failure to write the cache file + # there are many reasons, permission-denied, __pycache__ being a + # file etc. + return False + try: + fp.write(imp.get_magic()) + mtime = int(source_stat.mtime) + size = source_stat.size & 0xFFFFFFFF + fp.write(struct.pack(">", + ast.Add: "+", + ast.Sub: "-", + ast.Mult: "*", + ast.Div: "/", + ast.FloorDiv: "//", + ast.Mod: "%%", # escaped for string formatting + ast.Eq: "==", + ast.NotEq: "!=", + ast.Lt: "<", + ast.LtE: "<=", + ast.Gt: ">", + ast.GtE: ">=", + ast.Pow: "**", + ast.Is: "is", + ast.IsNot: "is not", + ast.In: "in", + ast.NotIn: "not in" +} +# Python 3.5+ compatibility +try: + binop_map[ast.MatMult] = "@" +except AttributeError: + pass + +# Python 3.4+ compatibility +if hasattr(ast, "NameConstant"): + _NameConstant = ast.NameConstant +else: + def _NameConstant(c): + return ast.Name(str(c), ast.Load()) + + +def set_location(node, lineno, col_offset): + """Set node location information recursively.""" + def _fix(node, lineno, col_offset): + if "lineno" in node._attributes: + node.lineno = lineno + if "col_offset" in node._attributes: + node.col_offset = col_offset + for child in ast.iter_child_nodes(node): + _fix(child, lineno, col_offset) + _fix(node, lineno, col_offset) + return node + + +class AssertionRewriter(ast.NodeVisitor): + """Assertion rewriting implementation. + + The main entrypoint is to call .run() with an ast.Module instance, + this will then find all the assert statements and re-write them to + provide intermediate values and a detailed assertion error. See + http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html + for an overview of how this works. + + The entry point here is .run() which will iterate over all the + statements in an ast.Module and for each ast.Assert statement it + finds call .visit() with it. Then .visit_Assert() takes over and + is responsible for creating new ast statements to replace the + original assert statement: it re-writes the test of an assertion + to provide intermediate values and replace it with an if statement + which raises an assertion error with a detailed explanation in + case the expression is false. + + For this .visit_Assert() uses the visitor pattern to visit all the + AST nodes of the ast.Assert.test field, each visit call returning + an AST node and the corresponding explanation string. During this + state is kept in several instance attributes: + + :statements: All the AST statements which will replace the assert + statement. + + :variables: This is populated by .variable() with each variable + used by the statements so that they can all be set to None at + the end of the statements. + + :variable_counter: Counter to create new unique variables needed + by statements. Variables are created using .variable() and + have the form of "@py_assert0". + + :on_failure: The AST statements which will be executed if the + assertion test fails. This is the code which will construct + the failure message and raises the AssertionError. + + :explanation_specifiers: A dict filled by .explanation_param() + with %-formatting placeholders and their corresponding + expressions to use in the building of an assertion message. + This is used by .pop_format_context() to build a message. + + :stack: A stack of the explanation_specifiers dicts maintained by + .push_format_context() and .pop_format_context() which allows + to build another %-formatted string while already building one. + + This state is reset on every new assert statement visited and used + by the other visitors. + + """ + + def __init__(self, module_path, config): + super(AssertionRewriter, self).__init__() + self.module_path = module_path + self.config = config + + def run(self, mod): + """Find all assert statements in *mod* and rewrite them.""" + if not mod.body: + # Nothing to do. + return + # Insert some special imports at the top of the module but after any + # docstrings and __future__ imports. + aliases = [ast.alias(py.builtin.builtins.__name__, "@py_builtins"), + ast.alias("_pytest.assertion.rewrite", "@pytest_ar")] + expect_docstring = True + pos = 0 + lineno = 0 + for item in mod.body: + if (expect_docstring and isinstance(item, ast.Expr) and + isinstance(item.value, ast.Str)): + doc = item.value.s + if "PYTEST_DONT_REWRITE" in doc: + # The module has disabled assertion rewriting. + return + lineno += len(doc) - 1 + expect_docstring = False + elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or + item.module != "__future__"): + lineno = item.lineno + break + pos += 1 + imports = [ast.Import([alias], lineno=lineno, col_offset=0) + for alias in aliases] + mod.body[pos:pos] = imports + # Collect asserts. + nodes = [mod] + while nodes: + node = nodes.pop() + for name, field in ast.iter_fields(node): + if isinstance(field, list): + new = [] + for i, child in enumerate(field): + if isinstance(child, ast.Assert): + # Transform assert. + new.extend(self.visit(child)) + else: + new.append(child) + if isinstance(child, ast.AST): + nodes.append(child) + setattr(node, name, new) + elif (isinstance(field, ast.AST) and + # Don't recurse into expressions as they can't contain + # asserts. + not isinstance(field, ast.expr)): + nodes.append(field) + + def variable(self): + """Get a new variable.""" + # Use a character invalid in python identifiers to avoid clashing. + name = "@py_assert" + str(next(self.variable_counter)) + self.variables.append(name) + return name + + def assign(self, expr): + """Give *expr* a name.""" + name = self.variable() + self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr)) + return ast.Name(name, ast.Load()) + + def display(self, expr): + """Call py.io.saferepr on the expression.""" + return self.helper("saferepr", expr) + + def helper(self, name, *args): + """Call a helper in this module.""" + py_name = ast.Name("@pytest_ar", ast.Load()) + attr = ast.Attribute(py_name, "_" + name, ast.Load()) + return ast_Call(attr, list(args), []) + + def builtin(self, name): + """Return the builtin called *name*.""" + builtin_name = ast.Name("@py_builtins", ast.Load()) + return ast.Attribute(builtin_name, name, ast.Load()) + + def explanation_param(self, expr): + """Return a new named %-formatting placeholder for expr. + + This creates a %-formatting placeholder for expr in the + current formatting context, e.g. ``%(py0)s``. The placeholder + and expr are placed in the current format context so that it + can be used on the next call to .pop_format_context(). + + """ + specifier = "py" + str(next(self.variable_counter)) + self.explanation_specifiers[specifier] = expr + return "%(" + specifier + ")s" + + def push_format_context(self): + """Create a new formatting context. + + The format context is used for when an explanation wants to + have a variable value formatted in the assertion message. In + this case the value required can be added using + .explanation_param(). Finally .pop_format_context() is used + to format a string of %-formatted values as added by + .explanation_param(). + + """ + self.explanation_specifiers = {} + self.stack.append(self.explanation_specifiers) + + def pop_format_context(self, expl_expr): + """Format the %-formatted string with current format context. + + The expl_expr should be an ast.Str instance constructed from + the %-placeholders created by .explanation_param(). This will + add the required code to format said string to .on_failure and + return the ast.Name instance of the formatted string. + + """ + current = self.stack.pop() + if self.stack: + self.explanation_specifiers = self.stack[-1] + keys = [ast.Str(key) for key in current.keys()] + format_dict = ast.Dict(keys, list(current.values())) + form = ast.BinOp(expl_expr, ast.Mod(), format_dict) + name = "@py_format" + str(next(self.variable_counter)) + self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form)) + return ast.Name(name, ast.Load()) + + def generic_visit(self, node): + """Handle expressions we don't have custom code for.""" + assert isinstance(node, ast.expr) + res = self.assign(node) + return res, self.explanation_param(self.display(res)) + + def visit_Assert(self, assert_): + """Return the AST statements to replace the ast.Assert instance. + + This re-writes the test of an assertion to provide + intermediate values and replace it with an if statement which + raises an assertion error with a detailed explanation in case + the expression is false. + + """ + if isinstance(assert_.test, ast.Tuple) and self.config is not None: + fslocation = (self.module_path, assert_.lineno) + self.config.warn('R1', 'assertion is always true, perhaps ' + 'remove parentheses?', fslocation=fslocation) + self.statements = [] + self.variables = [] + self.variable_counter = itertools.count() + self.stack = [] + self.on_failure = [] + self.push_format_context() + # Rewrite assert into a bunch of statements. + top_condition, explanation = self.visit(assert_.test) + # Create failure message. + body = self.on_failure + negation = ast.UnaryOp(ast.Not(), top_condition) + self.statements.append(ast.If(negation, body, [])) + if assert_.msg: + assertmsg = self.helper('format_assertmsg', assert_.msg) + explanation = "\n>assert " + explanation + else: + assertmsg = ast.Str("") + explanation = "assert " + explanation + template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation)) + msg = self.pop_format_context(template) + fmt = self.helper("format_explanation", msg) + err_name = ast.Name("AssertionError", ast.Load()) + exc = ast_Call(err_name, [fmt], []) + if sys.version_info[0] >= 3: + raise_ = ast.Raise(exc, None) + else: + raise_ = ast.Raise(exc, None, None) + body.append(raise_) + # Clear temporary variables by setting them to None. + if self.variables: + variables = [ast.Name(name, ast.Store()) + for name in self.variables] + clear = ast.Assign(variables, _NameConstant(None)) + self.statements.append(clear) + # Fix line numbers. + for stmt in self.statements: + set_location(stmt, assert_.lineno, assert_.col_offset) + return self.statements + + def visit_Name(self, name): + # Display the repr of the name if it's a local variable or + # _should_repr_global_name() thinks it's acceptable. + locs = ast_Call(self.builtin("locals"), [], []) + inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs]) + dorepr = self.helper("should_repr_global_name", name) + test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) + expr = ast.IfExp(test, self.display(name), ast.Str(name.id)) + return name, self.explanation_param(expr) + + def visit_BoolOp(self, boolop): + res_var = self.variable() + expl_list = self.assign(ast.List([], ast.Load())) + app = ast.Attribute(expl_list, "append", ast.Load()) + is_or = int(isinstance(boolop.op, ast.Or)) + body = save = self.statements + fail_save = self.on_failure + levels = len(boolop.values) - 1 + self.push_format_context() + # Process each operand, short-circuting if needed. + for i, v in enumerate(boolop.values): + if i: + fail_inner = [] + # cond is set in a prior loop iteration below + self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa + self.on_failure = fail_inner + self.push_format_context() + res, expl = self.visit(v) + body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) + expl_format = self.pop_format_context(ast.Str(expl)) + call = ast_Call(app, [expl_format], []) + self.on_failure.append(ast.Expr(call)) + if i < levels: + cond = res + if is_or: + cond = ast.UnaryOp(ast.Not(), cond) + inner = [] + self.statements.append(ast.If(cond, inner, [])) + self.statements = body = inner + self.statements = save + self.on_failure = fail_save + expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or)) + expl = self.pop_format_context(expl_template) + return ast.Name(res_var, ast.Load()), self.explanation_param(expl) + + def visit_UnaryOp(self, unary): + pattern = unary_map[unary.op.__class__] + operand_res, operand_expl = self.visit(unary.operand) + res = self.assign(ast.UnaryOp(unary.op, operand_res)) + return res, pattern % (operand_expl,) + + def visit_BinOp(self, binop): + symbol = binop_map[binop.op.__class__] + left_expr, left_expl = self.visit(binop.left) + right_expr, right_expl = self.visit(binop.right) + explanation = "(%s %s %s)" % (left_expl, symbol, right_expl) + res = self.assign(ast.BinOp(left_expr, binop.op, right_expr)) + return res, explanation + + def visit_Call_35(self, call): + """ + visit `ast.Call` nodes on Python3.5 and after + """ + new_func, func_expl = self.visit(call.func) + arg_expls = [] + new_args = [] + new_kwargs = [] + for arg in call.args: + res, expl = self.visit(arg) + arg_expls.append(expl) + new_args.append(res) + for keyword in call.keywords: + res, expl = self.visit(keyword.value) + new_kwargs.append(ast.keyword(keyword.arg, res)) + if keyword.arg: + arg_expls.append(keyword.arg + "=" + expl) + else: ## **args have `arg` keywords with an .arg of None + arg_expls.append("**" + expl) + + expl = "%s(%s)" % (func_expl, ', '.join(arg_expls)) + new_call = ast.Call(new_func, new_args, new_kwargs) + res = self.assign(new_call) + res_expl = self.explanation_param(self.display(res)) + outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl) + return res, outer_expl + + def visit_Starred(self, starred): + # From Python 3.5, a Starred node can appear in a function call + res, expl = self.visit(starred.value) + return starred, '*' + expl + + def visit_Call_legacy(self, call): + """ + visit `ast.Call nodes on 3.4 and below` + """ + new_func, func_expl = self.visit(call.func) + arg_expls = [] + new_args = [] + new_kwargs = [] + new_star = new_kwarg = None + for arg in call.args: + res, expl = self.visit(arg) + new_args.append(res) + arg_expls.append(expl) + for keyword in call.keywords: + res, expl = self.visit(keyword.value) + new_kwargs.append(ast.keyword(keyword.arg, res)) + arg_expls.append(keyword.arg + "=" + expl) + if call.starargs: + new_star, expl = self.visit(call.starargs) + arg_expls.append("*" + expl) + if call.kwargs: + new_kwarg, expl = self.visit(call.kwargs) + arg_expls.append("**" + expl) + expl = "%s(%s)" % (func_expl, ', '.join(arg_expls)) + new_call = ast.Call(new_func, new_args, new_kwargs, + new_star, new_kwarg) + res = self.assign(new_call) + res_expl = self.explanation_param(self.display(res)) + outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl) + return res, outer_expl + + # ast.Call signature changed on 3.5, + # conditionally change which methods is named + # visit_Call depending on Python version + if sys.version_info >= (3, 5): + visit_Call = visit_Call_35 + else: + visit_Call = visit_Call_legacy + + + def visit_Attribute(self, attr): + if not isinstance(attr.ctx, ast.Load): + return self.generic_visit(attr) + value, value_expl = self.visit(attr.value) + res = self.assign(ast.Attribute(value, attr.attr, ast.Load())) + res_expl = self.explanation_param(self.display(res)) + pat = "%s\n{%s = %s.%s\n}" + expl = pat % (res_expl, res_expl, value_expl, attr.attr) + return res, expl + + def visit_Compare(self, comp): + self.push_format_context() + left_res, left_expl = self.visit(comp.left) + if isinstance(comp.left, (_ast.Compare, _ast.BoolOp)): + left_expl = "({0})".format(left_expl) + res_variables = [self.variable() for i in range(len(comp.ops))] + load_names = [ast.Name(v, ast.Load()) for v in res_variables] + store_names = [ast.Name(v, ast.Store()) for v in res_variables] + it = zip(range(len(comp.ops)), comp.ops, comp.comparators) + expls = [] + syms = [] + results = [left_res] + for i, op, next_operand in it: + next_res, next_expl = self.visit(next_operand) + if isinstance(next_operand, (_ast.Compare, _ast.BoolOp)): + next_expl = "({0})".format(next_expl) + results.append(next_res) + sym = binop_map[op.__class__] + syms.append(ast.Str(sym)) + expl = "%s %s %s" % (left_expl, sym, next_expl) + expls.append(ast.Str(expl)) + res_expr = ast.Compare(left_res, [op], [next_res]) + self.statements.append(ast.Assign([store_names[i]], res_expr)) + left_res, left_expl = next_res, next_expl + # Use pytest.assertion.util._reprcompare if that's available. + expl_call = self.helper("call_reprcompare", + ast.Tuple(syms, ast.Load()), + ast.Tuple(load_names, ast.Load()), + ast.Tuple(expls, ast.Load()), + ast.Tuple(results, ast.Load())) + if len(comp.ops) > 1: + res = ast.BoolOp(ast.And(), load_names) + else: + res = load_names[0] + return res, self.explanation_param(self.pop_format_context(expl_call)) diff --git a/venv/lib/python3.5/site-packages/_pytest/assertion/truncate.py b/venv/lib/python3.5/site-packages/_pytest/assertion/truncate.py new file mode 100644 index 0000000..1e13063 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/assertion/truncate.py @@ -0,0 +1,102 @@ +""" +Utilities for truncating assertion output. + +Current default behaviour is to truncate assertion explanations at +~8 terminal lines, unless running in "-vv" mode or running on CI. +""" +from __future__ import absolute_import, division, print_function +import os + +import py + + +DEFAULT_MAX_LINES = 8 +DEFAULT_MAX_CHARS = 8 * 80 +USAGE_MSG = "use '-vv' to show" + + +def truncate_if_required(explanation, item, max_length=None): + """ + Truncate this assertion explanation if the given test item is eligible. + """ + if _should_truncate_item(item): + return _truncate_explanation(explanation) + return explanation + + +def _should_truncate_item(item): + """ + Whether or not this test item is eligible for truncation. + """ + verbose = item.config.option.verbose + return verbose < 2 and not _running_on_ci() + + +def _running_on_ci(): + """Check if we're currently running on a CI system.""" + env_vars = ['CI', 'BUILD_NUMBER'] + return any(var in os.environ for var in env_vars) + + +def _truncate_explanation(input_lines, max_lines=None, max_chars=None): + """ + Truncate given list of strings that makes up the assertion explanation. + + Truncates to either 8 lines, or 640 characters - whichever the input reaches + first. The remaining lines will be replaced by a usage message. + """ + + if max_lines is None: + max_lines = DEFAULT_MAX_LINES + if max_chars is None: + max_chars = DEFAULT_MAX_CHARS + + # Check if truncation required + input_char_count = len("".join(input_lines)) + if len(input_lines) <= max_lines and input_char_count <= max_chars: + return input_lines + + # Truncate first to max_lines, and then truncate to max_chars if max_chars + # is exceeded. + truncated_explanation = input_lines[:max_lines] + truncated_explanation = _truncate_by_char_count(truncated_explanation, max_chars) + + # Add ellipsis to final line + truncated_explanation[-1] = truncated_explanation[-1] + "..." + + # Append useful message to explanation + truncated_line_count = len(input_lines) - len(truncated_explanation) + truncated_line_count += 1 # Account for the part-truncated final line + msg = '...Full output truncated' + if truncated_line_count == 1: + msg += ' ({0} line hidden)'.format(truncated_line_count) + else: + msg += ' ({0} lines hidden)'.format(truncated_line_count) + msg += ", {0}" .format(USAGE_MSG) + truncated_explanation.extend([ + py.builtin._totext(""), + py.builtin._totext(msg), + ]) + return truncated_explanation + + +def _truncate_by_char_count(input_lines, max_chars): + # Check if truncation required + if len("".join(input_lines)) <= max_chars: + return input_lines + + # Find point at which input length exceeds total allowed length + iterated_char_count = 0 + for iterated_index, input_line in enumerate(input_lines): + if iterated_char_count + len(input_line) > max_chars: + break + iterated_char_count += len(input_line) + + # Create truncated explanation with modified final line + truncated_result = input_lines[:iterated_index] + final_line = input_lines[iterated_index] + if final_line: + final_line_truncate_point = max_chars - iterated_char_count + final_line = final_line[:final_line_truncate_point] + truncated_result.append(final_line) + return truncated_result diff --git a/venv/lib/python3.5/site-packages/_pytest/assertion/util.py b/venv/lib/python3.5/site-packages/_pytest/assertion/util.py new file mode 100644 index 0000000..06eda8d --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/assertion/util.py @@ -0,0 +1,301 @@ +"""Utilities for assertion debugging""" +from __future__ import absolute_import, division, print_function +import pprint + +import _pytest._code +import py +try: + from collections import Sequence +except ImportError: + Sequence = list + + +u = py.builtin._totext + +# The _reprcompare attribute on the util module is used by the new assertion +# interpretation code and assertion rewriter to detect this plugin was +# loaded and in turn call the hooks defined here as part of the +# DebugInterpreter. +_reprcompare = None + + +# the re-encoding is needed for python2 repr +# with non-ascii characters (see issue 877 and 1379) +def ecu(s): + try: + return u(s, 'utf-8', 'replace') + except TypeError: + return s + + +def format_explanation(explanation): + """This formats an explanation + + Normally all embedded newlines are escaped, however there are + three exceptions: \n{, \n} and \n~. The first two are intended + cover nested explanations, see function and attribute explanations + for examples (.visit_Call(), visit_Attribute()). The last one is + for when one explanation needs to span multiple lines, e.g. when + displaying diffs. + """ + explanation = ecu(explanation) + lines = _split_explanation(explanation) + result = _format_lines(lines) + return u('\n').join(result) + + +def _split_explanation(explanation): + """Return a list of individual lines in the explanation + + This will return a list of lines split on '\n{', '\n}' and '\n~'. + Any other newlines will be escaped and appear in the line as the + literal '\n' characters. + """ + raw_lines = (explanation or u('')).split('\n') + lines = [raw_lines[0]] + for l in raw_lines[1:]: + if l and l[0] in ['{', '}', '~', '>']: + lines.append(l) + else: + lines[-1] += '\\n' + l + return lines + + +def _format_lines(lines): + """Format the individual lines + + This will replace the '{', '}' and '~' characters of our mini + formatting language with the proper 'where ...', 'and ...' and ' + + ...' text, taking care of indentation along the way. + + Return a list of formatted lines. + """ + result = lines[:1] + stack = [0] + stackcnt = [0] + for line in lines[1:]: + if line.startswith('{'): + if stackcnt[-1]: + s = u('and ') + else: + s = u('where ') + stack.append(len(result)) + stackcnt[-1] += 1 + stackcnt.append(0) + result.append(u(' +') + u(' ')*(len(stack)-1) + s + line[1:]) + elif line.startswith('}'): + stack.pop() + stackcnt.pop() + result[stack[-1]] += line[1:] + else: + assert line[0] in ['~', '>'] + stack[-1] += 1 + indent = len(stack) if line.startswith('~') else len(stack) - 1 + result.append(u(' ')*indent + line[1:]) + assert len(stack) == 1 + return result + + +# Provide basestring in python3 +try: + basestring = basestring +except NameError: + basestring = str + + +def assertrepr_compare(config, op, left, right): + """Return specialised explanations for some operators/operands""" + width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op + left_repr = py.io.saferepr(left, maxsize=int(width//2)) + right_repr = py.io.saferepr(right, maxsize=width-len(left_repr)) + + summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr)) + + issequence = lambda x: (isinstance(x, (list, tuple, Sequence)) and + not isinstance(x, basestring)) + istext = lambda x: isinstance(x, basestring) + isdict = lambda x: isinstance(x, dict) + isset = lambda x: isinstance(x, (set, frozenset)) + + def isiterable(obj): + try: + iter(obj) + return not istext(obj) + except TypeError: + return False + + verbose = config.getoption('verbose') + explanation = None + try: + if op == '==': + if istext(left) and istext(right): + explanation = _diff_text(left, right, verbose) + else: + if issequence(left) and issequence(right): + explanation = _compare_eq_sequence(left, right, verbose) + elif isset(left) and isset(right): + explanation = _compare_eq_set(left, right, verbose) + elif isdict(left) and isdict(right): + explanation = _compare_eq_dict(left, right, verbose) + if isiterable(left) and isiterable(right): + expl = _compare_eq_iterable(left, right, verbose) + if explanation is not None: + explanation.extend(expl) + else: + explanation = expl + elif op == 'not in': + if istext(left) and istext(right): + explanation = _notin_text(left, right, verbose) + except Exception: + explanation = [ + u('(pytest_assertion plugin: representation of details failed. ' + 'Probably an object has a faulty __repr__.)'), + u(_pytest._code.ExceptionInfo())] + + if not explanation: + return None + + return [summary] + explanation + + +def _diff_text(left, right, verbose=False): + """Return the explanation for the diff between text or bytes + + Unless --verbose is used this will skip leading and trailing + characters which are identical to keep the diff minimal. + + If the input are bytes they will be safely converted to text. + """ + from difflib import ndiff + explanation = [] + if isinstance(left, py.builtin.bytes): + left = u(repr(left)[1:-1]).replace(r'\n', '\n') + if isinstance(right, py.builtin.bytes): + right = u(repr(right)[1:-1]).replace(r'\n', '\n') + if not verbose: + i = 0 # just in case left or right has zero length + for i in range(min(len(left), len(right))): + if left[i] != right[i]: + break + if i > 42: + i -= 10 # Provide some context + explanation = [u('Skipping %s identical leading ' + 'characters in diff, use -v to show') % i] + left = left[i:] + right = right[i:] + if len(left) == len(right): + for i in range(len(left)): + if left[-i] != right[-i]: + break + if i > 42: + i -= 10 # Provide some context + explanation += [u('Skipping %s identical trailing ' + 'characters in diff, use -v to show') % i] + left = left[:-i] + right = right[:-i] + keepends = True + explanation += [line.strip('\n') + for line in ndiff(left.splitlines(keepends), + right.splitlines(keepends))] + return explanation + + +def _compare_eq_iterable(left, right, verbose=False): + if not verbose: + return [u('Use -v to get the full diff')] + # dynamic import to speedup pytest + import difflib + + try: + left_formatting = pprint.pformat(left).splitlines() + right_formatting = pprint.pformat(right).splitlines() + explanation = [u('Full diff:')] + except Exception: + # hack: PrettyPrinter.pformat() in python 2 fails when formatting items that can't be sorted(), ie, calling + # sorted() on a list would raise. See issue #718. + # As a workaround, the full diff is generated by using the repr() string of each item of each container. + left_formatting = sorted(repr(x) for x in left) + right_formatting = sorted(repr(x) for x in right) + explanation = [u('Full diff (fallback to calling repr on each item):')] + explanation.extend(line.strip() for line in difflib.ndiff(left_formatting, right_formatting)) + return explanation + + +def _compare_eq_sequence(left, right, verbose=False): + explanation = [] + for i in range(min(len(left), len(right))): + if left[i] != right[i]: + explanation += [u('At index %s diff: %r != %r') + % (i, left[i], right[i])] + break + if len(left) > len(right): + explanation += [u('Left contains more items, first extra item: %s') + % py.io.saferepr(left[len(right)],)] + elif len(left) < len(right): + explanation += [ + u('Right contains more items, first extra item: %s') % + py.io.saferepr(right[len(left)],)] + return explanation + + +def _compare_eq_set(left, right, verbose=False): + explanation = [] + diff_left = left - right + diff_right = right - left + if diff_left: + explanation.append(u('Extra items in the left set:')) + for item in diff_left: + explanation.append(py.io.saferepr(item)) + if diff_right: + explanation.append(u('Extra items in the right set:')) + for item in diff_right: + explanation.append(py.io.saferepr(item)) + return explanation + + +def _compare_eq_dict(left, right, verbose=False): + explanation = [] + common = set(left).intersection(set(right)) + same = dict((k, left[k]) for k in common if left[k] == right[k]) + if same and verbose < 2: + explanation += [u('Omitting %s identical items, use -vv to show') % + len(same)] + elif same: + explanation += [u('Common items:')] + explanation += pprint.pformat(same).splitlines() + diff = set(k for k in common if left[k] != right[k]) + if diff: + explanation += [u('Differing items:')] + for k in diff: + explanation += [py.io.saferepr({k: left[k]}) + ' != ' + + py.io.saferepr({k: right[k]})] + extra_left = set(left) - set(right) + if extra_left: + explanation.append(u('Left contains more items:')) + explanation.extend(pprint.pformat( + dict((k, left[k]) for k in extra_left)).splitlines()) + extra_right = set(right) - set(left) + if extra_right: + explanation.append(u('Right contains more items:')) + explanation.extend(pprint.pformat( + dict((k, right[k]) for k in extra_right)).splitlines()) + return explanation + + +def _notin_text(term, text, verbose=False): + index = text.find(term) + head = text[:index] + tail = text[index+len(term):] + correct_text = head + tail + diff = _diff_text(correct_text, text, verbose) + newdiff = [u('%s is contained here:') % py.io.saferepr(term, maxsize=42)] + for line in diff: + if line.startswith(u('Skipping')): + continue + if line.startswith(u('- ')): + continue + if line.startswith(u('+ ')): + newdiff.append(u(' ') + line[2:]) + else: + newdiff.append(line) + return newdiff diff --git a/venv/lib/python3.5/site-packages/_pytest/cacheprovider.py b/venv/lib/python3.5/site-packages/_pytest/cacheprovider.py new file mode 100644 index 0000000..7fc08ff --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/cacheprovider.py @@ -0,0 +1,245 @@ +""" +merged implementation of the cache provider + +the name cache was not chosen to ensure pluggy automatically +ignores the external pytest-cache +""" +from __future__ import absolute_import, division, print_function +import py +import pytest +import json +from os.path import sep as _sep, altsep as _altsep + + +class Cache(object): + def __init__(self, config): + self.config = config + self._cachedir = config.rootdir.join(".cache") + self.trace = config.trace.root.get("cache") + if config.getvalue("cacheclear"): + self.trace("clearing cachedir") + if self._cachedir.check(): + self._cachedir.remove() + self._cachedir.mkdir() + + def makedir(self, name): + """ return a directory path object with the given name. If the + directory does not yet exist, it will be created. You can use it + to manage files likes e. g. store/retrieve database + dumps across test sessions. + + :param name: must be a string not containing a ``/`` separator. + Make sure the name contains your plugin or application + identifiers to prevent clashes with other cache users. + """ + if _sep in name or _altsep is not None and _altsep in name: + raise ValueError("name is not allowed to contain path separators") + return self._cachedir.ensure_dir("d", name) + + def _getvaluepath(self, key): + return self._cachedir.join('v', *key.split('/')) + + def get(self, key, default): + """ return cached value for the given key. If no value + was yet cached or the value cannot be read, the specified + default is returned. + + :param key: must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param default: must be provided in case of a cache-miss or + invalid cache values. + + """ + path = self._getvaluepath(key) + if path.check(): + try: + with path.open("r") as f: + return json.load(f) + except ValueError: + self.trace("cache-invalid at %s" % (path,)) + return default + + def set(self, key, value): + """ save value for the given key. + + :param key: must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param value: must be of any combination of basic + python types, including nested types + like e. g. lists of dictionaries. + """ + path = self._getvaluepath(key) + try: + path.dirpath().ensure_dir() + except (py.error.EEXIST, py.error.EACCES): + self.config.warn( + code='I9', message='could not create cache path %s' % (path,) + ) + return + try: + f = path.open('w') + except py.error.ENOTDIR: + self.config.warn( + code='I9', message='cache could not write path %s' % (path,)) + else: + with f: + self.trace("cache-write %s: %r" % (key, value,)) + json.dump(value, f, indent=2, sort_keys=True) + + +class LFPlugin: + """ Plugin which implements the --lf (run last-failing) option """ + def __init__(self, config): + self.config = config + active_keys = 'lf', 'failedfirst' + self.active = any(config.getvalue(key) for key in active_keys) + if self.active: + self.lastfailed = config.cache.get("cache/lastfailed", {}) + else: + self.lastfailed = {} + + def pytest_report_header(self): + if self.active: + if not self.lastfailed: + mode = "run all (no recorded failures)" + else: + mode = "rerun last %d failures%s" % ( + len(self.lastfailed), + " first" if self.config.getvalue("failedfirst") else "") + return "run-last-failure: %s" % mode + + def pytest_runtest_logreport(self, report): + if report.failed and "xfail" not in report.keywords: + self.lastfailed[report.nodeid] = True + elif not report.failed: + if report.when == "call": + self.lastfailed.pop(report.nodeid, None) + + def pytest_collectreport(self, report): + passed = report.outcome in ('passed', 'skipped') + if passed: + if report.nodeid in self.lastfailed: + self.lastfailed.pop(report.nodeid) + self.lastfailed.update( + (item.nodeid, True) + for item in report.result) + else: + self.lastfailed[report.nodeid] = True + + def pytest_collection_modifyitems(self, session, config, items): + if self.active and self.lastfailed: + previously_failed = [] + previously_passed = [] + for item in items: + if item.nodeid in self.lastfailed: + previously_failed.append(item) + else: + previously_passed.append(item) + if not previously_failed and previously_passed: + # running a subset of all tests with recorded failures outside + # of the set of tests currently executing + pass + elif self.config.getvalue("lf"): + items[:] = previously_failed + config.hook.pytest_deselected(items=previously_passed) + else: + items[:] = previously_failed + previously_passed + + def pytest_sessionfinish(self, session): + config = self.config + if config.getvalue("cacheshow") or hasattr(config, "slaveinput"): + return + prev_failed = config.cache.get("cache/lastfailed", None) is not None + if (session.testscollected and prev_failed) or self.lastfailed: + config.cache.set("cache/lastfailed", self.lastfailed) + + +def pytest_addoption(parser): + group = parser.getgroup("general") + group.addoption( + '--lf', '--last-failed', action='store_true', dest="lf", + help="rerun only the tests that failed " + "at the last run (or all if none failed)") + group.addoption( + '--ff', '--failed-first', action='store_true', dest="failedfirst", + help="run all tests but run the last failures first. " + "This may re-order tests and thus lead to " + "repeated fixture setup/teardown") + group.addoption( + '--cache-show', action='store_true', dest="cacheshow", + help="show cache contents, don't perform collection or tests") + group.addoption( + '--cache-clear', action='store_true', dest="cacheclear", + help="remove all cache contents at start of test run.") + + +def pytest_cmdline_main(config): + if config.option.cacheshow: + from _pytest.main import wrap_session + return wrap_session(config, cacheshow) + + + +@pytest.hookimpl(tryfirst=True) +def pytest_configure(config): + config.cache = Cache(config) + config.pluginmanager.register(LFPlugin(config), "lfplugin") + + +@pytest.fixture +def cache(request): + """ + Return a cache object that can persist state between testing sessions. + + cache.get(key, default) + cache.set(key, value) + + Keys must be a ``/`` separated value, where the first part is usually the + name of your plugin or application to avoid clashes with other cache users. + + Values can be any object handled by the json stdlib module. + """ + return request.config.cache + + +def pytest_report_header(config): + if config.option.verbose: + relpath = py.path.local().bestrelpath(config.cache._cachedir) + return "cachedir: %s" % relpath + + +def cacheshow(config, session): + from pprint import pprint + tw = py.io.TerminalWriter() + tw.line("cachedir: " + str(config.cache._cachedir)) + if not config.cache._cachedir.check(): + tw.line("cache is empty") + return 0 + dummy = object() + basedir = config.cache._cachedir + vdir = basedir.join("v") + tw.sep("-", "cache values") + for valpath in sorted(vdir.visit(lambda x: x.isfile())): + key = valpath.relto(vdir).replace(valpath.sep, "/") + val = config.cache.get(key, dummy) + if val is dummy: + tw.line("%s contains unreadable content, " + "will be ignored" % key) + else: + tw.line("%s contains:" % key) + stream = py.io.TextIO() + pprint(val, stream=stream) + for line in stream.getvalue().splitlines(): + tw.line(" " + line) + + ddir = basedir.join("d") + if ddir.isdir() and ddir.listdir(): + tw.sep("-", "cache directories") + for p in sorted(basedir.join("d").visit()): + #if p.check(dir=1): + # print("%s/" % p.relto(basedir)) + if p.isfile(): + key = p.relto(basedir) + tw.line("%s is a file of length %d" % ( + key, p.size())) + return 0 diff --git a/venv/lib/python3.5/site-packages/_pytest/capture.py b/venv/lib/python3.5/site-packages/_pytest/capture.py new file mode 100644 index 0000000..3661f26 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/capture.py @@ -0,0 +1,542 @@ +""" +per-test stdout/stderr capturing mechanism. + +""" +from __future__ import absolute_import, division, print_function + +import contextlib +import sys +import os +import io +from io import UnsupportedOperation +from tempfile import TemporaryFile + +import py +import pytest +from _pytest.compat import CaptureIO + +unicode = py.builtin.text + +patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} + + +def pytest_addoption(parser): + group = parser.getgroup("general") + group._addoption( + '--capture', action="store", + default="fd" if hasattr(os, "dup") else "sys", + metavar="method", choices=['fd', 'sys', 'no'], + help="per-test capturing method: one of fd|sys|no.") + group._addoption( + '-s', action="store_const", const="no", dest="capture", + help="shortcut for --capture=no.") + + +@pytest.hookimpl(hookwrapper=True) +def pytest_load_initial_conftests(early_config, parser, args): + ns = early_config.known_args_namespace + if ns.capture == "fd": + _py36_windowsconsoleio_workaround() + _readline_workaround() + pluginmanager = early_config.pluginmanager + capman = CaptureManager(ns.capture) + pluginmanager.register(capman, "capturemanager") + + # make sure that capturemanager is properly reset at final shutdown + early_config.add_cleanup(capman.reset_capturings) + + # make sure logging does not raise exceptions at the end + def silence_logging_at_shutdown(): + if "logging" in sys.modules: + sys.modules["logging"].raiseExceptions = False + early_config.add_cleanup(silence_logging_at_shutdown) + + # finally trigger conftest loading but while capturing (issue93) + capman.init_capturings() + outcome = yield + out, err = capman.suspendcapture() + if outcome.excinfo is not None: + sys.stdout.write(out) + sys.stderr.write(err) + + +class CaptureManager: + def __init__(self, method): + self._method = method + + def _getcapture(self, method): + if method == "fd": + return MultiCapture(out=True, err=True, Capture=FDCapture) + elif method == "sys": + return MultiCapture(out=True, err=True, Capture=SysCapture) + elif method == "no": + return MultiCapture(out=False, err=False, in_=False) + else: + raise ValueError("unknown capturing method: %r" % method) + + def init_capturings(self): + assert not hasattr(self, "_capturing") + self._capturing = self._getcapture(self._method) + self._capturing.start_capturing() + + def reset_capturings(self): + cap = self.__dict__.pop("_capturing", None) + if cap is not None: + cap.pop_outerr_to_orig() + cap.stop_capturing() + + def resumecapture(self): + self._capturing.resume_capturing() + + def suspendcapture(self, in_=False): + self.deactivate_funcargs() + cap = getattr(self, "_capturing", None) + if cap is not None: + try: + outerr = cap.readouterr() + finally: + cap.suspend_capturing(in_=in_) + return outerr + + def activate_funcargs(self, pyfuncitem): + capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None) + if capfuncarg is not None: + capfuncarg._start() + self._capfuncarg = capfuncarg + + def deactivate_funcargs(self): + capfuncarg = self.__dict__.pop("_capfuncarg", None) + if capfuncarg is not None: + capfuncarg.close() + + @pytest.hookimpl(hookwrapper=True) + def pytest_make_collect_report(self, collector): + if isinstance(collector, pytest.File): + self.resumecapture() + outcome = yield + out, err = self.suspendcapture() + rep = outcome.get_result() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + else: + yield + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_setup(self, item): + self.resumecapture() + yield + self.suspendcapture_item(item, "setup") + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_call(self, item): + self.resumecapture() + self.activate_funcargs(item) + yield + #self.deactivate_funcargs() called from suspendcapture() + self.suspendcapture_item(item, "call") + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_teardown(self, item): + self.resumecapture() + yield + self.suspendcapture_item(item, "teardown") + + @pytest.hookimpl(tryfirst=True) + def pytest_keyboard_interrupt(self, excinfo): + self.reset_capturings() + + @pytest.hookimpl(tryfirst=True) + def pytest_internalerror(self, excinfo): + self.reset_capturings() + + def suspendcapture_item(self, item, when, in_=False): + out, err = self.suspendcapture(in_=in_) + item.add_report_section(when, "stdout", out) + item.add_report_section(when, "stderr", err) + + +error_capsysfderror = "cannot use capsys and capfd at the same time" + + +@pytest.fixture +def capsys(request): + """Enable capturing of writes to sys.stdout/sys.stderr and make + captured output available via ``capsys.readouterr()`` method calls + which return a ``(out, err)`` tuple. + """ + if "capfd" in request.fixturenames: + raise request.raiseerror(error_capsysfderror) + request.node._capfuncarg = c = CaptureFixture(SysCapture, request) + return c + +@pytest.fixture +def capfd(request): + """Enable capturing of writes to file descriptors 1 and 2 and make + captured output available via ``capfd.readouterr()`` method calls + which return a ``(out, err)`` tuple. + """ + if "capsys" in request.fixturenames: + request.raiseerror(error_capsysfderror) + if not hasattr(os, 'dup'): + pytest.skip("capfd funcarg needs os.dup") + request.node._capfuncarg = c = CaptureFixture(FDCapture, request) + return c + + +class CaptureFixture: + def __init__(self, captureclass, request): + self.captureclass = captureclass + self.request = request + + def _start(self): + self._capture = MultiCapture(out=True, err=True, in_=False, + Capture=self.captureclass) + self._capture.start_capturing() + + def close(self): + cap = self.__dict__.pop("_capture", None) + if cap is not None: + self._outerr = cap.pop_outerr_to_orig() + cap.stop_capturing() + + def readouterr(self): + try: + return self._capture.readouterr() + except AttributeError: + return self._outerr + + @contextlib.contextmanager + def disabled(self): + capmanager = self.request.config.pluginmanager.getplugin('capturemanager') + capmanager.suspendcapture_item(self.request.node, "call", in_=True) + try: + yield + finally: + capmanager.resumecapture() + + +def safe_text_dupfile(f, mode, default_encoding="UTF8"): + """ return a open text file object that's a duplicate of f on the + FD-level if possible. + """ + encoding = getattr(f, "encoding", None) + try: + fd = f.fileno() + except Exception: + if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"): + # we seem to have a text stream, let's just use it + return f + else: + newfd = os.dup(fd) + if "b" not in mode: + mode += "b" + f = os.fdopen(newfd, mode, 0) # no buffering + return EncodedFile(f, encoding or default_encoding) + + +class EncodedFile(object): + errors = "strict" # possibly needed by py3 code (issue555) + def __init__(self, buffer, encoding): + self.buffer = buffer + self.encoding = encoding + + def write(self, obj): + if isinstance(obj, unicode): + obj = obj.encode(self.encoding, "replace") + self.buffer.write(obj) + + def writelines(self, linelist): + data = ''.join(linelist) + self.write(data) + + def __getattr__(self, name): + return getattr(object.__getattribute__(self, "buffer"), name) + + +class MultiCapture(object): + out = err = in_ = None + + def __init__(self, out=True, err=True, in_=True, Capture=None): + if in_: + self.in_ = Capture(0) + if out: + self.out = Capture(1) + if err: + self.err = Capture(2) + + def start_capturing(self): + if self.in_: + self.in_.start() + if self.out: + self.out.start() + if self.err: + self.err.start() + + def pop_outerr_to_orig(self): + """ pop current snapshot out/err capture and flush to orig streams. """ + out, err = self.readouterr() + if out: + self.out.writeorg(out) + if err: + self.err.writeorg(err) + return out, err + + def suspend_capturing(self, in_=False): + if self.out: + self.out.suspend() + if self.err: + self.err.suspend() + if in_ and self.in_: + self.in_.suspend() + self._in_suspended = True + + def resume_capturing(self): + if self.out: + self.out.resume() + if self.err: + self.err.resume() + if hasattr(self, "_in_suspended"): + self.in_.resume() + del self._in_suspended + + def stop_capturing(self): + """ stop capturing and reset capturing streams """ + if hasattr(self, '_reset'): + raise ValueError("was already stopped") + self._reset = True + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: + self.in_.done() + + def readouterr(self): + """ return snapshot unicode value of stdout/stderr capturings. """ + return (self.out.snap() if self.out is not None else "", + self.err.snap() if self.err is not None else "") + +class NoCapture: + __init__ = start = done = suspend = resume = lambda *args: None + +class FDCapture: + """ Capture IO to/from a given os-level filedescriptor. """ + + def __init__(self, targetfd, tmpfile=None): + self.targetfd = targetfd + try: + self.targetfd_save = os.dup(self.targetfd) + except OSError: + self.start = lambda: None + self.done = lambda: None + else: + if targetfd == 0: + assert not tmpfile, "cannot set tmpfile with stdin" + tmpfile = open(os.devnull, "r") + self.syscapture = SysCapture(targetfd) + else: + if tmpfile is None: + f = TemporaryFile() + with f: + tmpfile = safe_text_dupfile(f, mode="wb+") + if targetfd in patchsysdict: + self.syscapture = SysCapture(targetfd, tmpfile) + else: + self.syscapture = NoCapture() + self.tmpfile = tmpfile + self.tmpfile_fd = tmpfile.fileno() + + def __repr__(self): + return "" % (self.targetfd, self.targetfd_save) + + def start(self): + """ Start capturing on targetfd using memorized tmpfile. """ + try: + os.fstat(self.targetfd_save) + except (AttributeError, OSError): + raise ValueError("saved filedescriptor not valid anymore") + os.dup2(self.tmpfile_fd, self.targetfd) + self.syscapture.start() + + def snap(self): + f = self.tmpfile + f.seek(0) + res = f.read() + if res: + enc = getattr(f, "encoding", None) + if enc and isinstance(res, bytes): + res = py.builtin._totext(res, enc, "replace") + f.truncate(0) + f.seek(0) + return res + return '' + + def done(self): + """ stop capturing, restore streams, return original capture file, + seeked to position zero. """ + targetfd_save = self.__dict__.pop("targetfd_save") + os.dup2(targetfd_save, self.targetfd) + os.close(targetfd_save) + self.syscapture.done() + self.tmpfile.close() + + def suspend(self): + self.syscapture.suspend() + os.dup2(self.targetfd_save, self.targetfd) + + def resume(self): + self.syscapture.resume() + os.dup2(self.tmpfile_fd, self.targetfd) + + def writeorg(self, data): + """ write to original file descriptor. """ + if py.builtin._istext(data): + data = data.encode("utf8") # XXX use encoding of original stream + os.write(self.targetfd_save, data) + + +class SysCapture: + def __init__(self, fd, tmpfile=None): + name = patchsysdict[fd] + self._old = getattr(sys, name) + self.name = name + if tmpfile is None: + if name == "stdin": + tmpfile = DontReadFromInput() + else: + tmpfile = CaptureIO() + self.tmpfile = tmpfile + + def start(self): + setattr(sys, self.name, self.tmpfile) + + def snap(self): + f = self.tmpfile + res = f.getvalue() + f.truncate(0) + f.seek(0) + return res + + def done(self): + setattr(sys, self.name, self._old) + del self._old + self.tmpfile.close() + + def suspend(self): + setattr(sys, self.name, self._old) + + def resume(self): + setattr(sys, self.name, self.tmpfile) + + def writeorg(self, data): + self._old.write(data) + self._old.flush() + + +class DontReadFromInput: + """Temporary stub class. Ideally when stdin is accessed, the + capturing should be turned off, with possibly all data captured + so far sent to the screen. This should be configurable, though, + because in automated test runs it is better to crash than + hang indefinitely. + """ + + encoding = None + + def read(self, *args): + raise IOError("reading from stdin while output is captured") + readline = read + readlines = read + __iter__ = read + + def fileno(self): + raise UnsupportedOperation("redirected stdin is pseudofile, " + "has no fileno()") + + def isatty(self): + return False + + def close(self): + pass + + @property + def buffer(self): + if sys.version_info >= (3,0): + return self + else: + raise AttributeError('redirected stdin has no attribute buffer') + + +def _readline_workaround(): + """ + Ensure readline is imported so that it attaches to the correct stdio + handles on Windows. + + Pdb uses readline support where available--when not running from the Python + prompt, the readline module is not imported until running the pdb REPL. If + running pytest with the --pdb option this means the readline module is not + imported until after I/O capture has been started. + + This is a problem for pyreadline, which is often used to implement readline + support on Windows, as it does not attach to the correct handles for stdout + and/or stdin if they have been redirected by the FDCapture mechanism. This + workaround ensures that readline is imported before I/O capture is setup so + that it can attach to the actual stdin/out for the console. + + See https://github.com/pytest-dev/pytest/pull/1281 + """ + + if not sys.platform.startswith('win32'): + return + try: + import readline # noqa + except ImportError: + pass + + +def _py36_windowsconsoleio_workaround(): + """ + Python 3.6 implemented unicode console handling for Windows. This works + by reading/writing to the raw console handle using + ``{Read,Write}ConsoleW``. + + The problem is that we are going to ``dup2`` over the stdio file + descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the + handles used by Python to write to the console. Though there is still some + weirdness and the console handle seems to only be closed randomly and not + on the first call to ``CloseHandle``, or maybe it gets reopened with the + same handle value when we suspend capturing. + + The workaround in this case will reopen stdio with a different fd which + also means a different handle by replicating the logic in + "Py_lifecycle.c:initstdio/create_stdio". + + See https://github.com/pytest-dev/py/issues/103 + """ + if not sys.platform.startswith('win32') or sys.version_info[:2] < (3, 6): + return + + buffered = hasattr(sys.stdout.buffer, 'raw') + raw_stdout = sys.stdout.buffer.raw if buffered else sys.stdout.buffer + + if not isinstance(raw_stdout, io._WindowsConsoleIO): + return + + def _reopen_stdio(f, mode): + if not buffered and mode[0] == 'w': + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), + f.encoding, + f.errors, + f.newlines, + f.line_buffering) + + sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb') + sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb') + sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb') diff --git a/venv/lib/python3.5/site-packages/_pytest/compat.py b/venv/lib/python3.5/site-packages/_pytest/compat.py new file mode 100644 index 0000000..8c200af --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/compat.py @@ -0,0 +1,307 @@ +""" +python version compatibility code +""" +from __future__ import absolute_import, division, print_function +import sys +import inspect +import types +import re +import functools + +import py + +import _pytest + + + +try: + import enum +except ImportError: # pragma: no cover + # Only available in Python 3.4+ or as a backport + enum = None + + +_PY3 = sys.version_info > (3, 0) +_PY2 = not _PY3 + + +NoneType = type(None) +NOTSET = object() + +PY35 = sys.version_info[:2] >= (3, 5) +PY36 = sys.version_info[:2] >= (3, 6) +MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError' + +if hasattr(inspect, 'signature'): + def _format_args(func): + return str(inspect.signature(func)) +else: + def _format_args(func): + return inspect.formatargspec(*inspect.getargspec(func)) + +isfunction = inspect.isfunction +isclass = inspect.isclass +# used to work around a python2 exception info leak +exc_clear = getattr(sys, 'exc_clear', lambda: None) +# The type of re.compile objects is not exposed in Python. +REGEX_TYPE = type(re.compile('')) + + +def is_generator(func): + genfunc = inspect.isgeneratorfunction(func) + return genfunc and not iscoroutinefunction(func) + + +def iscoroutinefunction(func): + """Return True if func is a decorated coroutine function. + + Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly, + which in turns also initializes the "logging" module as side-effect (see issue #8). + """ + return (getattr(func, '_is_coroutine', False) or + (hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(func))) + + +def getlocation(function, curdir): + import inspect + fn = py.path.local(inspect.getfile(function)) + lineno = py.builtin._getcode(function).co_firstlineno + if fn.relto(curdir): + fn = fn.relto(curdir) + return "%s:%d" %(fn, lineno+1) + + +def num_mock_patch_args(function): + """ return number of arguments used up by mock arguments (if any) """ + patchings = getattr(function, "patchings", None) + if not patchings: + return 0 + mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None)) + if mock is not None: + return len([p for p in patchings + if not p.attribute_name and p.new is mock.DEFAULT]) + return len(patchings) + + +def getfuncargnames(function, startindex=None): + # XXX merge with main.py's varnames + #assert not isclass(function) + realfunction = function + while hasattr(realfunction, "__wrapped__"): + realfunction = realfunction.__wrapped__ + if startindex is None: + startindex = inspect.ismethod(function) and 1 or 0 + if realfunction != function: + startindex += num_mock_patch_args(function) + function = realfunction + if isinstance(function, functools.partial): + argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0] + partial = function + argnames = argnames[len(partial.args):] + if partial.keywords: + for kw in partial.keywords: + argnames.remove(kw) + else: + argnames = inspect.getargs(_pytest._code.getrawcode(function))[0] + defaults = getattr(function, 'func_defaults', + getattr(function, '__defaults__', None)) or () + numdefaults = len(defaults) + if numdefaults: + return tuple(argnames[startindex:-numdefaults]) + return tuple(argnames[startindex:]) + + + +if sys.version_info[:2] == (2, 6): + def isclass(object): + """ Return true if the object is a class. Overrides inspect.isclass for + python 2.6 because it will return True for objects which always return + something on __getattr__ calls (see #1035). + Backport of https://hg.python.org/cpython/rev/35bf8f7a8edc + """ + return isinstance(object, (type, types.ClassType)) + + +if _PY3: + import codecs + imap = map + STRING_TYPES = bytes, str + UNICODE_TYPES = str, + + def _escape_strings(val): + """If val is pure ascii, returns it as a str(). Otherwise, escapes + bytes objects into a sequence of escaped bytes: + + b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6' + + and escapes unicode objects into a sequence of escaped unicode + ids, e.g.: + + '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944' + + note: + the obvious "v.decode('unicode-escape')" will return + valid utf-8 unicode if it finds them in bytes, but we + want to return escaped bytes for any byte, even if they match + a utf-8 string. + + """ + if isinstance(val, bytes): + if val: + # source: http://goo.gl/bGsnwC + encoded_bytes, _ = codecs.escape_encode(val) + return encoded_bytes.decode('ascii') + else: + # empty bytes crashes codecs.escape_encode (#1087) + return '' + else: + return val.encode('unicode_escape').decode('ascii') +else: + STRING_TYPES = bytes, str, unicode + UNICODE_TYPES = unicode, + + from itertools import imap # NOQA + + def _escape_strings(val): + """In py2 bytes and str are the same type, so return if it's a bytes + object, return it unchanged if it is a full ascii string, + otherwise escape it into its binary form. + + If it's a unicode string, change the unicode characters into + unicode escapes. + + """ + if isinstance(val, bytes): + try: + return val.encode('ascii') + except UnicodeDecodeError: + return val.encode('string-escape') + else: + return val.encode('unicode-escape') + + +def get_real_func(obj): + """ gets the real function object of the (possibly) wrapped object by + functools.wraps or functools.partial. + """ + start_obj = obj + for i in range(100): + new_obj = getattr(obj, '__wrapped__', None) + if new_obj is None: + break + obj = new_obj + else: + raise ValueError( + ("could not find real function of {start}" + "\nstopped at {current}").format( + start=py.io.saferepr(start_obj), + current=py.io.saferepr(obj))) + if isinstance(obj, functools.partial): + obj = obj.func + return obj + + +def getfslineno(obj): + # xxx let decorators etc specify a sane ordering + obj = get_real_func(obj) + if hasattr(obj, 'place_as'): + obj = obj.place_as + fslineno = _pytest._code.getfslineno(obj) + assert isinstance(fslineno[1], int), obj + return fslineno + + +def getimfunc(func): + try: + return func.__func__ + except AttributeError: + try: + return func.im_func + except AttributeError: + return func + + +def safe_getattr(object, name, default): + """ Like getattr but return default upon any Exception. + + Attribute access can potentially fail for 'evil' Python objects. + See issue #214. + """ + try: + return getattr(object, name, default) + except Exception: + return default + + +def _is_unittest_unexpected_success_a_failure(): + """Return if the test suite should fail if a @expectedFailure unittest test PASSES. + + From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful: + Changed in version 3.4: Returns False if there were any + unexpectedSuccesses from tests marked with the expectedFailure() decorator. + """ + return sys.version_info >= (3, 4) + + +if _PY3: + def safe_str(v): + """returns v as string""" + return str(v) +else: + def safe_str(v): + """returns v as string, converting to ascii if necessary""" + try: + return str(v) + except UnicodeError: + if not isinstance(v, unicode): + v = unicode(v) + errors = 'replace' + return v.encode('utf-8', errors) + + +COLLECT_FAKEMODULE_ATTRIBUTES = ( + 'Collector', + 'Module', + 'Generator', + 'Function', + 'Instance', + 'Session', + 'Item', + 'Class', + 'File', + '_fillfuncargs', +) + + +def _setup_collect_fakemodule(): + from types import ModuleType + import pytest + pytest.collect = ModuleType('pytest.collect') + pytest.collect.__all__ = [] # used for setns + for attr in COLLECT_FAKEMODULE_ATTRIBUTES: + setattr(pytest.collect, attr, getattr(pytest, attr)) + + +if _PY2: + from py.io import TextIO as CaptureIO +else: + import io + + class CaptureIO(io.TextIOWrapper): + def __init__(self): + super(CaptureIO, self).__init__( + io.BytesIO(), + encoding='UTF-8', newline='', write_through=True, + ) + + def getvalue(self): + return self.buffer.getvalue().decode('UTF-8') + +class FuncargnamesCompatAttr(object): + """ helper class so that Metafunc, Function and FixtureRequest + don't need to each define the "funcargnames" compatibility attribute. + """ + @property + def funcargnames(self): + """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" + return self.fixturenames diff --git a/venv/lib/python3.5/site-packages/_pytest/config.py b/venv/lib/python3.5/site-packages/_pytest/config.py new file mode 100644 index 0000000..dadd5ca --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/config.py @@ -0,0 +1,1397 @@ +""" command line options, ini-file and conftest.py processing. """ +from __future__ import absolute_import, division, print_function +import argparse +import shlex +import traceback +import types +import warnings + +import py +# DON't import pytest here because it causes import cycle troubles +import sys +import os +import _pytest._code +import _pytest.hookspec # the extension point definitions +import _pytest.assertion +from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker +from _pytest.compat import safe_str + +hookimpl = HookimplMarker("pytest") +hookspec = HookspecMarker("pytest") + +# pytest startup +# + + +class ConftestImportFailure(Exception): + def __init__(self, path, excinfo): + Exception.__init__(self, path, excinfo) + self.path = path + self.excinfo = excinfo + + def __str__(self): + etype, evalue, etb = self.excinfo + formatted = traceback.format_tb(etb) + # The level of the tracebacks we want to print is hand crafted :( + return repr(evalue) + '\n' + ''.join(formatted[2:]) + + +def main(args=None, plugins=None): + """ return exit code, after performing an in-process test run. + + :arg args: list of command line arguments. + + :arg plugins: list of plugin objects to be auto-registered during + initialization. + """ + try: + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure as e: + tw = py.io.TerminalWriter(sys.stderr) + for line in traceback.format_exception(*e.excinfo): + tw.line(line.rstrip(), red=True) + tw.line("ERROR: could not load %s\n" % (e.path), red=True) + return 4 + else: + try: + return config.hook.pytest_cmdline_main(config=config) + finally: + config._ensure_unconfigure() + except UsageError as e: + for msg in e.args: + sys.stderr.write("ERROR: %s\n" %(msg,)) + return 4 + +class cmdline: # compatibility namespace + main = staticmethod(main) + + +class UsageError(Exception): + """ error in pytest usage or invocation""" + + +class PrintHelp(Exception): + """Raised when pytest should print it's help to skip the rest of the + argument parsing and validation.""" + pass + + +def filename_arg(path, optname): + """ Argparse type validator for filename arguments. + + :path: path of filename + :optname: name of the option + """ + if os.path.isdir(path): + raise UsageError("{0} must be a filename, given: {1}".format(optname, path)) + return path + + +def directory_arg(path, optname): + """Argparse type validator for directory arguments. + + :path: path of directory + :optname: name of the option + """ + if not os.path.isdir(path): + raise UsageError("{0} must be a directory, given: {1}".format(optname, path)) + return path + + +_preinit = [] + +default_plugins = ( + "mark main terminal runner python fixtures debugging unittest capture skipping " + "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion " + "junitxml resultlog doctest cacheprovider freeze_support " + "setuponly setupplan warnings").split() + + +builtin_plugins = set(default_plugins) +builtin_plugins.add("pytester") + + +def _preloadplugins(): + assert not _preinit + _preinit.append(get_config()) + +def get_config(): + if _preinit: + return _preinit.pop(0) + # subsequent calls to main will create a fresh instance + pluginmanager = PytestPluginManager() + config = Config(pluginmanager) + for spec in default_plugins: + pluginmanager.import_plugin(spec) + return config + +def get_plugin_manager(): + """ + Obtain a new instance of the + :py:class:`_pytest.config.PytestPluginManager`, with default plugins + already loaded. + + This function can be used by integration with other tools, like hooking + into pytest to run tests into an IDE. + """ + return get_config().pluginmanager + +def _prepareconfig(args=None, plugins=None): + warning = None + if args is None: + args = sys.argv[1:] + elif isinstance(args, py.path.local): + args = [str(args)] + elif not isinstance(args, (tuple, list)): + if not isinstance(args, str): + raise ValueError("not a string or argument list: %r" % (args,)) + args = shlex.split(args, posix=sys.platform != "win32") + from _pytest import deprecated + warning = deprecated.MAIN_STR_ARGS + config = get_config() + pluginmanager = config.pluginmanager + try: + if plugins: + for plugin in plugins: + if isinstance(plugin, py.builtin._basestring): + pluginmanager.consider_pluginarg(plugin) + else: + pluginmanager.register(plugin) + if warning: + config.warn('C1', warning) + return pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args) + except BaseException: + config._ensure_unconfigure() + raise + + +class PytestPluginManager(PluginManager): + """ + Overwrites :py:class:`pluggy.PluginManager <_pytest.vendored_packages.pluggy.PluginManager>` to add pytest-specific + functionality: + + * loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and + ``pytest_plugins`` global variables found in plugins being loaded; + * ``conftest.py`` loading during start-up; + """ + def __init__(self): + super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_") + self._conftest_plugins = set() + + # state related to local conftest plugins + self._path2confmods = {} + self._conftestpath2mod = {} + self._confcutdir = None + self._noconftest = False + self._duplicatepaths = set() + + self.add_hookspecs(_pytest.hookspec) + self.register(self) + if os.environ.get('PYTEST_DEBUG'): + err = sys.stderr + encoding = getattr(err, 'encoding', 'utf8') + try: + err = py.io.dupfile(err, encoding=encoding) + except Exception: + pass + self.trace.root.setwriter(err.write) + self.enable_tracing() + + # Config._consider_importhook will set a real object if required. + self.rewrite_hook = _pytest.assertion.DummyRewriteHook() + + def addhooks(self, module_or_class): + """ + .. deprecated:: 2.8 + + Use :py:meth:`pluggy.PluginManager.add_hookspecs <_pytest.vendored_packages.pluggy.PluginManager.add_hookspecs>` instead. + """ + warning = dict(code="I2", + fslocation=_pytest._code.getfslineno(sys._getframe(1)), + nodeid=None, + message="use pluginmanager.add_hookspecs instead of " + "deprecated addhooks() method.") + self._warn(warning) + return self.add_hookspecs(module_or_class) + + def parse_hookimpl_opts(self, plugin, name): + # pytest hooks are always prefixed with pytest_ + # so we avoid accessing possibly non-readable attributes + # (see issue #1073) + if not name.startswith("pytest_"): + return + # ignore some historic special names which can not be hooks anyway + if name == "pytest_plugins" or name.startswith("pytest_funcarg__"): + return + + method = getattr(plugin, name) + opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name) + if opts is not None: + for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): + opts.setdefault(name, hasattr(method, name)) + return opts + + def parse_hookspec_opts(self, module_or_class, name): + opts = super(PytestPluginManager, self).parse_hookspec_opts( + module_or_class, name) + if opts is None: + method = getattr(module_or_class, name) + if name.startswith("pytest_"): + opts = {"firstresult": hasattr(method, "firstresult"), + "historic": hasattr(method, "historic")} + return opts + + def _verify_hook(self, hook, hookmethod): + super(PytestPluginManager, self)._verify_hook(hook, hookmethod) + if "__multicall__" in hookmethod.argnames: + fslineno = _pytest._code.getfslineno(hookmethod.function) + warning = dict(code="I1", + fslocation=fslineno, + nodeid=None, + message="%r hook uses deprecated __multicall__ " + "argument" % (hook.name)) + self._warn(warning) + + def register(self, plugin, name=None): + ret = super(PytestPluginManager, self).register(plugin, name) + if ret: + self.hook.pytest_plugin_registered.call_historic( + kwargs=dict(plugin=plugin, manager=self)) + + if isinstance(plugin, types.ModuleType): + self.consider_module(plugin) + return ret + + def getplugin(self, name): + # support deprecated naming because plugins (xdist e.g.) use it + return self.get_plugin(name) + + def hasplugin(self, name): + """Return True if the plugin with the given name is registered.""" + return bool(self.get_plugin(name)) + + def pytest_configure(self, config): + # XXX now that the pluginmanager exposes hookimpl(tryfirst...) + # we should remove tryfirst/trylast as markers + config.addinivalue_line("markers", + "tryfirst: mark a hook implementation function such that the " + "plugin machinery will try to call it first/as early as possible.") + config.addinivalue_line("markers", + "trylast: mark a hook implementation function such that the " + "plugin machinery will try to call it last/as late as possible.") + + def _warn(self, message): + kwargs = message if isinstance(message, dict) else { + 'code': 'I1', + 'message': message, + 'fslocation': None, + 'nodeid': None, + } + self.hook.pytest_logwarning.call_historic(kwargs=kwargs) + + # + # internal API for local conftest plugin handling + # + def _set_initial_conftests(self, namespace): + """ load initial conftest files given a preparsed "namespace". + As conftest files may add their own command line options + which have arguments ('--my-opt somepath') we might get some + false positives. All builtin and 3rd party plugins will have + been loaded, however, so common options will not confuse our logic + here. + """ + current = py.path.local() + self._confcutdir = current.join(namespace.confcutdir, abs=True) \ + if namespace.confcutdir else None + self._noconftest = namespace.noconftest + testpaths = namespace.file_or_dir + foundanchor = False + for path in testpaths: + path = str(path) + # remove node-id syntax + i = path.find("::") + if i != -1: + path = path[:i] + anchor = current.join(path, abs=1) + if exists(anchor): # we found some file object + self._try_load_conftest(anchor) + foundanchor = True + if not foundanchor: + self._try_load_conftest(current) + + def _try_load_conftest(self, anchor): + self._getconftestmodules(anchor) + # let's also consider test* subdirs + if anchor.check(dir=1): + for x in anchor.listdir("test*"): + if x.check(dir=1): + self._getconftestmodules(x) + + def _getconftestmodules(self, path): + if self._noconftest: + return [] + try: + return self._path2confmods[path] + except KeyError: + if path.isfile(): + clist = self._getconftestmodules(path.dirpath()) + else: + # XXX these days we may rather want to use config.rootdir + # and allow users to opt into looking into the rootdir parent + # directories instead of requiring to specify confcutdir + clist = [] + for parent in path.parts(): + if self._confcutdir and self._confcutdir.relto(parent): + continue + conftestpath = parent.join("conftest.py") + if conftestpath.isfile(): + mod = self._importconftest(conftestpath) + clist.append(mod) + + self._path2confmods[path] = clist + return clist + + def _rget_with_confmod(self, name, path): + modules = self._getconftestmodules(path) + for mod in reversed(modules): + try: + return mod, getattr(mod, name) + except AttributeError: + continue + raise KeyError(name) + + def _importconftest(self, conftestpath): + try: + return self._conftestpath2mod[conftestpath] + except KeyError: + pkgpath = conftestpath.pypkgpath() + if pkgpath is None: + _ensure_removed_sysmodule(conftestpath.purebasename) + try: + mod = conftestpath.pyimport() + except Exception: + raise ConftestImportFailure(conftestpath, sys.exc_info()) + + self._conftest_plugins.add(mod) + self._conftestpath2mod[conftestpath] = mod + dirpath = conftestpath.dirpath() + if dirpath in self._path2confmods: + for path, mods in self._path2confmods.items(): + if path and path.relto(dirpath) or path == dirpath: + assert mod not in mods + mods.append(mod) + self.trace("loaded conftestmodule %r" %(mod)) + self.consider_conftest(mod) + return mod + + # + # API for bootstrapping plugin loading + # + # + + def consider_preparse(self, args): + for opt1,opt2 in zip(args, args[1:]): + if opt1 == "-p": + self.consider_pluginarg(opt2) + + def consider_pluginarg(self, arg): + if arg.startswith("no:"): + name = arg[3:] + self.set_blocked(name) + if not name.startswith("pytest_"): + self.set_blocked("pytest_" + name) + else: + self.import_plugin(arg) + + def consider_conftest(self, conftestmodule): + self.register(conftestmodule, name=conftestmodule.__file__) + + def consider_env(self): + self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) + + def consider_module(self, mod): + self._import_plugin_specs(getattr(mod, 'pytest_plugins', [])) + + def _import_plugin_specs(self, spec): + plugins = _get_plugin_specs_as_list(spec) + for import_spec in plugins: + self.import_plugin(import_spec) + + def import_plugin(self, modname): + # most often modname refers to builtin modules, e.g. "pytester", + # "terminal" or "capture". Those plugins are registered under their + # basename for historic purposes but must be imported with the + # _pytest prefix. + assert isinstance(modname, (py.builtin.text, str)), "module name as text required, got %r" % modname + modname = str(modname) + if self.get_plugin(modname) is not None: + return + if modname in builtin_plugins: + importspec = "_pytest." + modname + else: + importspec = modname + self.rewrite_hook.mark_rewrite(importspec) + try: + __import__(importspec) + except ImportError as e: + new_exc = ImportError('Error importing plugin "%s": %s' % (modname, safe_str(e.args[0]))) + # copy over name and path attributes + for attr in ('name', 'path'): + if hasattr(e, attr): + setattr(new_exc, attr, getattr(e, attr)) + raise new_exc + except Exception as e: + import pytest + if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception): + raise + self._warn("skipped plugin %r: %s" %((modname, e.msg))) + else: + mod = sys.modules[importspec] + self.register(mod, modname) + + +def _get_plugin_specs_as_list(specs): + """ + Parses a list of "plugin specs" and returns a list of plugin names. + + Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in + which case it is returned as a list. Specs can also be `None` in which case an + empty list is returned. + """ + if specs is not None: + if isinstance(specs, str): + specs = specs.split(',') if specs else [] + if not isinstance(specs, (list, tuple)): + raise UsageError("Plugin specs must be a ','-separated string or a " + "list/tuple of strings for plugin names. Given: %r" % specs) + return list(specs) + return [] + + +class Parser: + """ Parser for command line arguments and ini-file values. + + :ivar extra_info: dict of generic param -> value to display in case + there's an error processing the command line arguments. + """ + + def __init__(self, usage=None, processopt=None): + self._anonymous = OptionGroup("custom options", parser=self) + self._groups = [] + self._processopt = processopt + self._usage = usage + self._inidict = {} + self._ininames = [] + self.extra_info = {} + + def processoption(self, option): + if self._processopt: + if option.dest: + self._processopt(option) + + def getgroup(self, name, description="", after=None): + """ get (or create) a named option Group. + + :name: name of the option group. + :description: long description for --help output. + :after: name of other group, used for ordering --help output. + + The returned group object has an ``addoption`` method with the same + signature as :py:func:`parser.addoption + <_pytest.config.Parser.addoption>` but will be shown in the + respective group in the output of ``pytest. --help``. + """ + for group in self._groups: + if group.name == name: + return group + group = OptionGroup(name, description, parser=self) + i = 0 + for i, grp in enumerate(self._groups): + if grp.name == after: + break + self._groups.insert(i+1, group) + return group + + def addoption(self, *opts, **attrs): + """ register a command line option. + + :opts: option names, can be short or long options. + :attrs: same attributes which the ``add_option()`` function of the + `argparse library + `_ + accepts. + + After command line parsing options are available on the pytest config + object via ``config.option.NAME`` where ``NAME`` is usually set + by passing a ``dest`` attribute, for example + ``addoption("--long", dest="NAME", ...)``. + """ + self._anonymous.addoption(*opts, **attrs) + + def parse(self, args, namespace=None): + from _pytest._argcomplete import try_argcomplete + self.optparser = self._getparser() + try_argcomplete(self.optparser) + return self.optparser.parse_args([str(x) for x in args], namespace=namespace) + + def _getparser(self): + from _pytest._argcomplete import filescompleter + optparser = MyOptionParser(self, self.extra_info) + groups = self._groups + [self._anonymous] + for group in groups: + if group.options: + desc = group.description or group.name + arggroup = optparser.add_argument_group(desc) + for option in group.options: + n = option.names() + a = option.attrs() + arggroup.add_argument(*n, **a) + # bash like autocompletion for dirs (appending '/') + optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter + return optparser + + def parse_setoption(self, args, option, namespace=None): + parsedoption = self.parse(args, namespace=namespace) + for name, value in parsedoption.__dict__.items(): + setattr(option, name, value) + return getattr(parsedoption, FILE_OR_DIR) + + def parse_known_args(self, args, namespace=None): + """parses and returns a namespace object with known arguments at this + point. + """ + return self.parse_known_and_unknown_args(args, namespace=namespace)[0] + + def parse_known_and_unknown_args(self, args, namespace=None): + """parses and returns a namespace object with known arguments, and + the remaining arguments unknown at this point. + """ + optparser = self._getparser() + args = [str(x) for x in args] + return optparser.parse_known_args(args, namespace=namespace) + + def addini(self, name, help, type=None, default=None): + """ register an ini-file option. + + :name: name of the ini-variable + :type: type of the variable, can be ``pathlist``, ``args``, ``linelist`` + or ``bool``. + :default: default value if no ini-file option exists but is queried. + + The value of ini-variables can be retrieved via a call to + :py:func:`config.getini(name) <_pytest.config.Config.getini>`. + """ + assert type in (None, "pathlist", "args", "linelist", "bool") + self._inidict[name] = (help, type, default) + self._ininames.append(name) + + +class ArgumentError(Exception): + """ + Raised if an Argument instance is created with invalid or + inconsistent arguments. + """ + + def __init__(self, msg, option): + self.msg = msg + self.option_id = str(option) + + def __str__(self): + if self.option_id: + return "option %s: %s" % (self.option_id, self.msg) + else: + return self.msg + + +class Argument: + """class that mimics the necessary behaviour of optparse.Option + + its currently a least effort implementation + and ignoring choices and integer prefixes + https://docs.python.org/3/library/optparse.html#optparse-standard-option-types + """ + _typ_map = { + 'int': int, + 'string': str, + 'float': float, + 'complex': complex, + } + + def __init__(self, *names, **attrs): + """store parms in private vars for use in add_argument""" + self._attrs = attrs + self._short_opts = [] + self._long_opts = [] + self.dest = attrs.get('dest') + if '%default' in (attrs.get('help') or ''): + warnings.warn( + 'pytest now uses argparse. "%default" should be' + ' changed to "%(default)s" ', + DeprecationWarning, + stacklevel=3) + try: + typ = attrs['type'] + except KeyError: + pass + else: + # this might raise a keyerror as well, don't want to catch that + if isinstance(typ, py.builtin._basestring): + if typ == 'choice': + warnings.warn( + 'type argument to addoption() is a string %r.' + ' For parsearg this is optional and when supplied' + ' should be a type.' + ' (options: %s)' % (typ, names), + DeprecationWarning, + stacklevel=3) + # argparse expects a type here take it from + # the type of the first element + attrs['type'] = type(attrs['choices'][0]) + else: + warnings.warn( + 'type argument to addoption() is a string %r.' + ' For parsearg this should be a type.' + ' (options: %s)' % (typ, names), + DeprecationWarning, + stacklevel=3) + attrs['type'] = Argument._typ_map[typ] + # used in test_parseopt -> test_parse_defaultgetter + self.type = attrs['type'] + else: + self.type = typ + try: + # attribute existence is tested in Config._processopt + self.default = attrs['default'] + except KeyError: + pass + self._set_opt_strings(names) + if not self.dest: + if self._long_opts: + self.dest = self._long_opts[0][2:].replace('-', '_') + else: + try: + self.dest = self._short_opts[0][1:] + except IndexError: + raise ArgumentError( + 'need a long or short option', self) + + def names(self): + return self._short_opts + self._long_opts + + def attrs(self): + # update any attributes set by processopt + attrs = 'default dest help'.split() + if self.dest: + attrs.append(self.dest) + for attr in attrs: + try: + self._attrs[attr] = getattr(self, attr) + except AttributeError: + pass + if self._attrs.get('help'): + a = self._attrs['help'] + a = a.replace('%default', '%(default)s') + #a = a.replace('%prog', '%(prog)s') + self._attrs['help'] = a + return self._attrs + + def _set_opt_strings(self, opts): + """directly from optparse + + might not be necessary as this is passed to argparse later on""" + for opt in opts: + if len(opt) < 2: + raise ArgumentError( + "invalid option string %r: " + "must be at least two characters long" % opt, self) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise ArgumentError( + "invalid short option string %r: " + "must be of the form -x, (x any non-dash char)" % opt, + self) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise ArgumentError( + "invalid long option string %r: " + "must start with --, followed by non-dash" % opt, + self) + self._long_opts.append(opt) + + def __repr__(self): + args = [] + if self._short_opts: + args += ['_short_opts: ' + repr(self._short_opts)] + if self._long_opts: + args += ['_long_opts: ' + repr(self._long_opts)] + args += ['dest: ' + repr(self.dest)] + if hasattr(self, 'type'): + args += ['type: ' + repr(self.type)] + if hasattr(self, 'default'): + args += ['default: ' + repr(self.default)] + return 'Argument({0})'.format(', '.join(args)) + + +class OptionGroup: + def __init__(self, name, description="", parser=None): + self.name = name + self.description = description + self.options = [] + self.parser = parser + + def addoption(self, *optnames, **attrs): + """ add an option to this group. + + if a shortened version of a long option is specified it will + be suppressed in the help. addoption('--twowords', '--two-words') + results in help showing '--two-words' only, but --twowords gets + accepted **and** the automatic destination is in args.twowords + """ + conflict = set(optnames).intersection( + name for opt in self.options for name in opt.names()) + if conflict: + raise ValueError("option names %s already added" % conflict) + option = Argument(*optnames, **attrs) + self._addoption_instance(option, shortupper=False) + + def _addoption(self, *optnames, **attrs): + option = Argument(*optnames, **attrs) + self._addoption_instance(option, shortupper=True) + + def _addoption_instance(self, option, shortupper=False): + if not shortupper: + for opt in option._short_opts: + if opt[0] == '-' and opt[1].islower(): + raise ValueError("lowercase shortoptions reserved") + if self.parser: + self.parser.processoption(option) + self.options.append(option) + + +class MyOptionParser(argparse.ArgumentParser): + def __init__(self, parser, extra_info=None): + if not extra_info: + extra_info = {} + self._parser = parser + argparse.ArgumentParser.__init__(self, usage=parser._usage, + add_help=False, formatter_class=DropShorterLongHelpFormatter) + # extra_info is a dict of (param -> value) to display if there's + # an usage error to provide more contextual information to the user + self.extra_info = extra_info + + def parse_args(self, args=None, namespace=None): + """allow splitting of positional arguments""" + args, argv = self.parse_known_args(args, namespace) + if argv: + for arg in argv: + if arg and arg[0] == '-': + lines = ['unrecognized arguments: %s' % (' '.join(argv))] + for k, v in sorted(self.extra_info.items()): + lines.append(' %s: %s' % (k, v)) + self.error('\n'.join(lines)) + getattr(args, FILE_OR_DIR).extend(argv) + return args + + +class DropShorterLongHelpFormatter(argparse.HelpFormatter): + """shorten help for long options that differ only in extra hyphens + + - collapse **long** options that are the same except for extra hyphens + - special action attribute map_long_option allows surpressing additional + long options + - shortcut if there are only two options and one of them is a short one + - cache result on action object as this is called at least 2 times + """ + def _format_action_invocation(self, action): + orgstr = argparse.HelpFormatter._format_action_invocation(self, action) + if orgstr and orgstr[0] != '-': # only optional arguments + return orgstr + res = getattr(action, '_formatted_action_invocation', None) + if res: + return res + options = orgstr.split(', ') + if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): + # a shortcut for '-h, --help' or '--abc', '-a' + action._formatted_action_invocation = orgstr + return orgstr + return_list = [] + option_map = getattr(action, 'map_long_option', {}) + if option_map is None: + option_map = {} + short_long = {} + for option in options: + if len(option) == 2 or option[2] == ' ': + continue + if not option.startswith('--'): + raise ArgumentError('long optional argument without "--": [%s]' + % (option), self) + xxoption = option[2:] + if xxoption.split()[0] not in option_map: + shortened = xxoption.replace('-', '') + if shortened not in short_long or \ + len(short_long[shortened]) < len(xxoption): + short_long[shortened] = xxoption + # now short_long has been filled out to the longest with dashes + # **and** we keep the right option ordering from add_argument + for option in options: # + if len(option) == 2 or option[2] == ' ': + return_list.append(option) + if option[2:] == short_long.get(option.replace('-', '')): + return_list.append(option.replace(' ', '=', 1)) + action._formatted_action_invocation = ', '.join(return_list) + return action._formatted_action_invocation + + + +def _ensure_removed_sysmodule(modname): + try: + del sys.modules[modname] + except KeyError: + pass + +class CmdOptions(object): + """ holds cmdline options as attributes.""" + def __init__(self, values=()): + self.__dict__.update(values) + def __repr__(self): + return "" %(self.__dict__,) + def copy(self): + return CmdOptions(self.__dict__) + +class Notset: + def __repr__(self): + return "" + + +notset = Notset() +FILE_OR_DIR = 'file_or_dir' + + +class Config(object): + """ access to configuration values, pluginmanager and plugin hooks. """ + + def __init__(self, pluginmanager): + #: access to command line option as attributes. + #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead + self.option = CmdOptions() + _a = FILE_OR_DIR + self._parser = Parser( + usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a), + processopt=self._processopt, + ) + #: a pluginmanager instance + self.pluginmanager = pluginmanager + self.trace = self.pluginmanager.trace.root.get("config") + self.hook = self.pluginmanager.hook + self._inicache = {} + self._override_ini = () + self._opt2dest = {} + self._cleanup = [] + self._warn = self.pluginmanager._warn + self.pluginmanager.register(self, "pytestconfig") + self._configured = False + + def do_setns(dic): + import pytest + setns(pytest, dic) + + self.hook.pytest_namespace.call_historic(do_setns, {}) + self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) + + def add_cleanup(self, func): + """ Add a function to be called when the config object gets out of + use (usually coninciding with pytest_unconfigure).""" + self._cleanup.append(func) + + def _do_configure(self): + assert not self._configured + self._configured = True + self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) + + def _ensure_unconfigure(self): + if self._configured: + self._configured = False + self.hook.pytest_unconfigure(config=self) + self.hook.pytest_configure._call_history = [] + while self._cleanup: + fin = self._cleanup.pop() + fin() + + def warn(self, code, message, fslocation=None, nodeid=None): + """ generate a warning for this test session. """ + self.hook.pytest_logwarning.call_historic(kwargs=dict( + code=code, message=message, + fslocation=fslocation, nodeid=nodeid)) + + def get_terminal_writer(self): + return self.pluginmanager.get_plugin("terminalreporter")._tw + + def pytest_cmdline_parse(self, pluginmanager, args): + # REF1 assert self == pluginmanager.config, (self, pluginmanager.config) + self.parse(args) + return self + + def notify_exception(self, excinfo, option=None): + if option and option.fulltrace: + style = "long" + else: + style = "native" + excrepr = excinfo.getrepr(funcargs=True, + showlocals=getattr(option, 'showlocals', False), + style=style, + ) + res = self.hook.pytest_internalerror(excrepr=excrepr, + excinfo=excinfo) + if not py.builtin.any(res): + for line in str(excrepr).split("\n"): + sys.stderr.write("INTERNALERROR> %s\n" %line) + sys.stderr.flush() + + def cwd_relative_nodeid(self, nodeid): + # nodeid's are relative to the rootpath, compute relative to cwd + if self.invocation_dir != self.rootdir: + fullpath = self.rootdir.join(nodeid) + nodeid = self.invocation_dir.bestrelpath(fullpath) + return nodeid + + @classmethod + def fromdictargs(cls, option_dict, args): + """ constructor useable for subprocesses. """ + config = get_config() + config.option.__dict__.update(option_dict) + config.parse(args, addopts=False) + for x in config.option.plugins: + config.pluginmanager.consider_pluginarg(x) + return config + + def _processopt(self, opt): + for name in opt._short_opts + opt._long_opts: + self._opt2dest[name] = opt.dest + + if hasattr(opt, 'default') and opt.dest: + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + + @hookimpl(trylast=True) + def pytest_load_initial_conftests(self, early_config): + self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) + + def _initini(self, args): + ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy()) + r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn) + self.rootdir, self.inifile, self.inicfg = r + self._parser.extra_info['rootdir'] = self.rootdir + self._parser.extra_info['inifile'] = self.inifile + self.invocation_dir = py.path.local() + self._parser.addini('addopts', 'extra command line options', 'args') + self._parser.addini('minversion', 'minimally required pytest version') + self._override_ini = ns.override_ini or () + + def _consider_importhook(self, args): + """Install the PEP 302 import hook if using assertion re-writing. + + Needs to parse the --assert= option from the commandline + and find all the installed plugins to mark them for re-writing + by the importhook. + """ + ns, unknown_args = self._parser.parse_known_and_unknown_args(args) + mode = ns.assertmode + if mode == 'rewrite': + try: + hook = _pytest.assertion.install_importhook(self) + except SystemError: + mode = 'plain' + else: + self._mark_plugins_for_rewrite(hook) + self._warn_about_missing_assertion(mode) + + def _mark_plugins_for_rewrite(self, hook): + """ + Given an importhook, mark for rewrite any top-level + modules or packages in the distribution package for + all pytest plugins. + """ + import pkg_resources + self.pluginmanager.rewrite_hook = hook + + # 'RECORD' available for plugins installed normally (pip install) + # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e) + # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa + # so it shouldn't be an issue + metadata_files = 'RECORD', 'SOURCES.txt' + + package_files = ( + entry.split(',')[0] + for entrypoint in pkg_resources.iter_entry_points('pytest11') + for metadata in metadata_files + for entry in entrypoint.dist._get_metadata(metadata) + ) + + for fn in package_files: + is_simple_module = os.sep not in fn and fn.endswith('.py') + is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py') + if is_simple_module: + module_name, ext = os.path.splitext(fn) + hook.mark_rewrite(module_name) + elif is_package: + package_name = os.path.dirname(fn) + hook.mark_rewrite(package_name) + + def _warn_about_missing_assertion(self, mode): + try: + assert False + except AssertionError: + pass + else: + if mode == 'plain': + sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED" + " and FAILING TESTS WILL PASS. Are you" + " using python -O?") + else: + sys.stderr.write("WARNING: assertions not in test modules or" + " plugins will be ignored" + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n") + + def _preparse(self, args, addopts=True): + self._initini(args) + if addopts: + args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args + args[:] = self.getini("addopts") + args + self._checkversion() + self._consider_importhook(args) + self.pluginmanager.consider_preparse(args) + self.pluginmanager.load_setuptools_entrypoints('pytest11') + self.pluginmanager.consider_env() + self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy()) + confcutdir = self.known_args_namespace.confcutdir + if self.known_args_namespace.confcutdir is None and self.inifile: + confcutdir = py.path.local(self.inifile).dirname + self.known_args_namespace.confcutdir = confcutdir + try: + self.hook.pytest_load_initial_conftests(early_config=self, + args=args, parser=self._parser) + except ConftestImportFailure: + e = sys.exc_info()[1] + if ns.help or ns.version: + # we don't want to prevent --help/--version to work + # so just let is pass and print a warning at the end + self._warn("could not load initial conftests (%s)\n" % e.path) + else: + raise + + def _checkversion(self): + import pytest + minver = self.inicfg.get('minversion', None) + if minver: + ver = minver.split(".") + myver = pytest.__version__.split(".") + if myver < ver: + raise pytest.UsageError( + "%s:%d: requires pytest-%s, actual pytest-%s'" %( + self.inicfg.config.path, self.inicfg.lineof('minversion'), + minver, pytest.__version__)) + + def parse(self, args, addopts=True): + # parse given cmdline arguments into this config object. + assert not hasattr(self, 'args'), ( + "can only parse cmdline args at most once per Config object") + self._origargs = args + self.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=self.pluginmanager)) + self._preparse(args, addopts=addopts) + # XXX deprecated hook: + self.hook.pytest_cmdline_preparse(config=self, args=args) + self._parser.after_preparse = True + try: + args = self._parser.parse_setoption(args, self.option, namespace=self.option) + if not args: + cwd = os.getcwd() + if cwd == self.rootdir: + args = self.getini('testpaths') + if not args: + args = [cwd] + self.args = args + except PrintHelp: + pass + + def addinivalue_line(self, name, line): + """ add a line to an ini-file option. The option must have been + declared but might not yet be set in which case the line becomes the + the first line in its value. """ + x = self.getini(name) + assert isinstance(x, list) + x.append(line) # modifies the cached list inline + + def getini(self, name): + """ return configuration value from an :ref:`ini file `. If the + specified name hasn't been registered through a prior + :py:func:`parser.addini <_pytest.config.Parser.addini>` + call (usually from a plugin), a ValueError is raised. """ + try: + return self._inicache[name] + except KeyError: + self._inicache[name] = val = self._getini(name) + return val + + def _getini(self, name): + try: + description, type, default = self._parser._inidict[name] + except KeyError: + raise ValueError("unknown configuration value: %r" %(name,)) + value = self._get_override_ini_value(name) + if value is None: + try: + value = self.inicfg[name] + except KeyError: + if default is not None: + return default + if type is None: + return '' + return [] + if type == "pathlist": + dp = py.path.local(self.inicfg.config.path).dirpath() + l = [] + for relpath in shlex.split(value): + l.append(dp.join(relpath, abs=True)) + return l + elif type == "args": + return shlex.split(value) + elif type == "linelist": + return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] + elif type == "bool": + return bool(_strtobool(value.strip())) + else: + assert type is None + return value + + def _getconftest_pathlist(self, name, path): + try: + mod, relroots = self.pluginmanager._rget_with_confmod(name, path) + except KeyError: + return None + modpath = py.path.local(mod.__file__).dirpath() + l = [] + for relroot in relroots: + if not isinstance(relroot, py.path.local): + relroot = relroot.replace("/", py.path.local.sep) + relroot = modpath.join(relroot, abs=True) + l.append(relroot) + return l + + def _get_override_ini_value(self, name): + value = None + # override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and + # and -o foo1=bar1 -o foo2=bar2 options + # always use the last item if multiple value set for same ini-name, + # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 + for ini_config_list in self._override_ini: + for ini_config in ini_config_list: + try: + (key, user_ini_value) = ini_config.split("=", 1) + except ValueError: + raise UsageError("-o/--override-ini expects option=value style.") + if key == name: + value = user_ini_value + return value + + def getoption(self, name, default=notset, skip=False): + """ return command line option value. + + :arg name: name of the option. You may also specify + the literal ``--OPT`` option instead of the "dest" option name. + :arg default: default value if no option of that name exists. + :arg skip: if True raise pytest.skip if option does not exists + or has a None value. + """ + name = self._opt2dest.get(name, name) + try: + val = getattr(self.option, name) + if val is None and skip: + raise AttributeError(name) + return val + except AttributeError: + if default is not notset: + return default + if skip: + import pytest + pytest.skip("no %r option found" %(name,)) + raise ValueError("no option named %r" % (name,)) + + def getvalue(self, name, path=None): + """ (deprecated, use getoption()) """ + return self.getoption(name) + + def getvalueorskip(self, name, path=None): + """ (deprecated, use getoption(skip=True)) """ + return self.getoption(name, skip=True) + +def exists(path, ignore=EnvironmentError): + try: + return path.check() + except ignore: + return False + +def getcfg(args, warnfunc=None): + """ + Search the list of arguments for a valid ini-file for pytest, + and return a tuple of (rootdir, inifile, cfg-dict). + + note: warnfunc is an optional function used to warn + about ini-files that use deprecated features. + This parameter should be removed when pytest + adopts standard deprecation warnings (#1804). + """ + from _pytest.deprecated import SETUP_CFG_PYTEST + inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"] + args = [x for x in args if not str(x).startswith("-")] + if not args: + args = [py.path.local()] + for arg in args: + arg = py.path.local(arg) + for base in arg.parts(reverse=True): + for inibasename in inibasenames: + p = base.join(inibasename) + if exists(p): + iniconfig = py.iniconfig.IniConfig(p) + if 'pytest' in iniconfig.sections: + if inibasename == 'setup.cfg' and warnfunc: + warnfunc('C1', SETUP_CFG_PYTEST) + return base, p, iniconfig['pytest'] + if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections: + return base, p, iniconfig['tool:pytest'] + elif inibasename == "pytest.ini": + # allowed to be empty + return base, p, {} + return None, None, None + + +def get_common_ancestor(paths): + common_ancestor = None + for path in paths: + if not path.exists(): + continue + if common_ancestor is None: + common_ancestor = path + else: + if path.relto(common_ancestor) or path == common_ancestor: + continue + elif common_ancestor.relto(path): + common_ancestor = path + else: + shared = path.common(common_ancestor) + if shared is not None: + common_ancestor = shared + if common_ancestor is None: + common_ancestor = py.path.local() + elif common_ancestor.isfile(): + common_ancestor = common_ancestor.dirpath() + return common_ancestor + + +def get_dirs_from_args(args): + def is_option(x): + return str(x).startswith('-') + + def get_file_part_from_node_id(x): + return str(x).split('::')[0] + + def get_dir_from_path(path): + if path.isdir(): + return path + return py.path.local(path.dirname) + + # These look like paths but may not exist + possible_paths = ( + py.path.local(get_file_part_from_node_id(arg)) + for arg in args + if not is_option(arg) + ) + + return [ + get_dir_from_path(path) + for path in possible_paths + if path.exists() + ] + + +def determine_setup(inifile, args, warnfunc=None): + dirs = get_dirs_from_args(args) + if inifile: + iniconfig = py.iniconfig.IniConfig(inifile) + try: + inicfg = iniconfig["pytest"] + except KeyError: + inicfg = None + rootdir = get_common_ancestor(dirs) + else: + ancestor = get_common_ancestor(dirs) + rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc) + if rootdir is None: + for rootdir in ancestor.parts(reverse=True): + if rootdir.join("setup.py").exists(): + break + else: + rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc) + if rootdir is None: + rootdir = get_common_ancestor([py.path.local(), ancestor]) + is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep + if is_fs_root: + rootdir = ancestor + return rootdir, inifile, inicfg or {} + + +def setns(obj, dic): + import pytest + for name, value in dic.items(): + if isinstance(value, dict): + mod = getattr(obj, name, None) + if mod is None: + modname = "pytest.%s" % name + mod = types.ModuleType(modname) + sys.modules[modname] = mod + mod.__all__ = [] + setattr(obj, name, mod) + obj.__all__.append(name) + setns(mod, value) + else: + setattr(obj, name, value) + obj.__all__.append(name) + #if obj != pytest: + # pytest.__all__.append(name) + setattr(pytest, name, value) + + +def create_terminal_writer(config, *args, **kwargs): + """Create a TerminalWriter instance configured according to the options + in the config object. Every code which requires a TerminalWriter object + and has access to a config object should use this function. + """ + tw = py.io.TerminalWriter(*args, **kwargs) + if config.option.color == 'yes': + tw.hasmarkup = True + if config.option.color == 'no': + tw.hasmarkup = False + return tw + + +def _strtobool(val): + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + + .. note:: copied from distutils.util + """ + val = val.lower() + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return 1 + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return 0 + else: + raise ValueError("invalid truth value %r" % (val,)) diff --git a/venv/lib/python3.5/site-packages/_pytest/debugging.py b/venv/lib/python3.5/site-packages/_pytest/debugging.py new file mode 100644 index 0000000..73a0a2e --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/debugging.py @@ -0,0 +1,123 @@ +""" interactive debugging with PDB, the Python Debugger. """ +from __future__ import absolute_import, division, print_function +import pdb +import sys + + + +def pytest_addoption(parser): + group = parser.getgroup("general") + group._addoption( + '--pdb', dest="usepdb", action="store_true", + help="start the interactive Python debugger on errors.") + group._addoption( + '--pdbcls', dest="usepdb_cls", metavar="modulename:classname", + help="start a custom interactive Python debugger on errors. " + "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb") + + +def pytest_configure(config): + if config.getvalue("usepdb_cls"): + modname, classname = config.getvalue("usepdb_cls").split(":") + __import__(modname) + pdb_cls = getattr(sys.modules[modname], classname) + else: + pdb_cls = pdb.Pdb + + if config.getvalue("usepdb"): + config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') + + old = (pdb.set_trace, pytestPDB._pluginmanager) + + def fin(): + pdb.set_trace, pytestPDB._pluginmanager = old + pytestPDB._config = None + pytestPDB._pdb_cls = pdb.Pdb + + pdb.set_trace = pytestPDB.set_trace + pytestPDB._pluginmanager = config.pluginmanager + pytestPDB._config = config + pytestPDB._pdb_cls = pdb_cls + config._cleanup.append(fin) + +class pytestPDB: + """ Pseudo PDB that defers to the real pdb. """ + _pluginmanager = None + _config = None + _pdb_cls = pdb.Pdb + + @classmethod + def set_trace(cls): + """ invoke PDB set_trace debugging, dropping any IO capturing. """ + import _pytest.config + frame = sys._getframe().f_back + if cls._pluginmanager is not None: + capman = cls._pluginmanager.getplugin("capturemanager") + if capman: + capman.suspendcapture(in_=True) + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + tw.sep(">", "PDB set_trace (IO-capturing turned off)") + cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config) + cls._pdb_cls().set_trace(frame) + + +class PdbInvoke: + def pytest_exception_interact(self, node, call, report): + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + out, err = capman.suspendcapture(in_=True) + sys.stdout.write(out) + sys.stdout.write(err) + _enter_pdb(node, call.excinfo, report) + + def pytest_internalerror(self, excrepr, excinfo): + for line in str(excrepr).split("\n"): + sys.stderr.write("INTERNALERROR> %s\n" % line) + sys.stderr.flush() + tb = _postmortem_traceback(excinfo) + post_mortem(tb) + + +def _enter_pdb(node, excinfo, rep): + # XXX we re-use the TerminalReporter's terminalwriter + # because this seems to avoid some encoding related troubles + # for not completely clear reasons. + tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw.line() + tw.sep(">", "traceback") + rep.toterminal(tw) + tw.sep(">", "entering PDB") + tb = _postmortem_traceback(excinfo) + post_mortem(tb) + rep._pdbshown = True + return rep + + +def _postmortem_traceback(excinfo): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + from doctest import UnexpectedException + if isinstance(excinfo.value, UnexpectedException): + return excinfo.value.exc_info[2] + else: + return excinfo._excinfo[2] + + +def _find_last_non_hidden_frame(stack): + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get("__tracebackhide__", False): + i -= 1 + return i + + +def post_mortem(t): + class Pdb(pytestPDB._pdb_cls): + def get_stack(self, f, t): + stack, i = pdb.Pdb.get_stack(self, f, t) + if f is None: + i = _find_last_non_hidden_frame(stack) + return stack, i + p = Pdb() + p.reset() + p.interaction(None, t) diff --git a/venv/lib/python3.5/site-packages/_pytest/deprecated.py b/venv/lib/python3.5/site-packages/_pytest/deprecated.py new file mode 100644 index 0000000..e75ff09 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/deprecated.py @@ -0,0 +1,24 @@ +""" +This module contains deprecation messages and bits of code used elsewhere in the codebase +that is planned to be removed in the next pytest release. + +Keeping it in a central location makes it easy to track what is deprecated and should +be removed when the time comes. +""" +from __future__ import absolute_import, division, print_function + +MAIN_STR_ARGS = 'passing a string to pytest.main() is deprecated, ' \ + 'pass a list of arguments instead.' + +YIELD_TESTS = 'yield tests are deprecated, and scheduled to be removed in pytest 4.0' + +FUNCARG_PREFIX = ( + '{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated ' + 'and scheduled to be removed in pytest 4.0. ' + 'Please remove the prefix and use the @pytest.fixture decorator instead.') + +SETUP_CFG_PYTEST = '[pytest] section in setup.cfg files is deprecated, use [tool:pytest] instead.' + +GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue" + +RESULT_LOG = '--result-log is deprecated and scheduled for removal in pytest 4.0' diff --git a/venv/lib/python3.5/site-packages/_pytest/doctest.py b/venv/lib/python3.5/site-packages/_pytest/doctest.py new file mode 100644 index 0000000..fde6dd7 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/doctest.py @@ -0,0 +1,360 @@ +""" discover and run doctests in modules and test files.""" +from __future__ import absolute_import, division, print_function + +import traceback + +import pytest +from _pytest._code.code import ExceptionInfo, ReprFileLocation, TerminalRepr +from _pytest.fixtures import FixtureRequest + + +DOCTEST_REPORT_CHOICE_NONE = 'none' +DOCTEST_REPORT_CHOICE_CDIFF = 'cdiff' +DOCTEST_REPORT_CHOICE_NDIFF = 'ndiff' +DOCTEST_REPORT_CHOICE_UDIFF = 'udiff' +DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = 'only_first_failure' + +DOCTEST_REPORT_CHOICES = ( + DOCTEST_REPORT_CHOICE_NONE, + DOCTEST_REPORT_CHOICE_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF, + DOCTEST_REPORT_CHOICE_UDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE, +) + +def pytest_addoption(parser): + parser.addini('doctest_optionflags', 'option flags for doctests', + type="args", default=["ELLIPSIS"]) + parser.addini("doctest_encoding", 'encoding used for doctest files', default="utf-8") + group = parser.getgroup("collect") + group.addoption("--doctest-modules", + action="store_true", default=False, + help="run doctests in all .py modules", + dest="doctestmodules") + group.addoption("--doctest-report", + type=str.lower, default="udiff", + help="choose another output format for diffs on doctest failure", + choices=DOCTEST_REPORT_CHOICES, + dest="doctestreport") + group.addoption("--doctest-glob", + action="append", default=[], metavar="pat", + help="doctests file matching pattern, default: test*.txt", + dest="doctestglob") + group.addoption("--doctest-ignore-import-errors", + action="store_true", default=False, + help="ignore doctest ImportErrors", + dest="doctest_ignore_import_errors") + + +def pytest_collect_file(path, parent): + config = parent.config + if path.ext == ".py": + if config.option.doctestmodules: + return DoctestModule(path, parent) + elif _is_doctest(config, path, parent): + return DoctestTextfile(path, parent) + + +def _is_doctest(config, path, parent): + if path.ext in ('.txt', '.rst') and parent.session.isinitpath(path): + return True + globs = config.getoption("doctestglob") or ['test*.txt'] + for glob in globs: + if path.check(fnmatch=glob): + return True + return False + + +class ReprFailDoctest(TerminalRepr): + + def __init__(self, reprlocation, lines): + self.reprlocation = reprlocation + self.lines = lines + + def toterminal(self, tw): + for line in self.lines: + tw.line(line) + self.reprlocation.toterminal(tw) + + +class DoctestItem(pytest.Item): + def __init__(self, name, parent, runner=None, dtest=None): + super(DoctestItem, self).__init__(name, parent) + self.runner = runner + self.dtest = dtest + self.obj = None + self.fixture_request = None + + def setup(self): + if self.dtest is not None: + self.fixture_request = _setup_fixtures(self) + globs = dict(getfixture=self.fixture_request.getfixturevalue) + for name, value in self.fixture_request.getfixturevalue('doctest_namespace').items(): + globs[name] = value + self.dtest.globs.update(globs) + + def runtest(self): + _check_all_skipped(self.dtest) + self.runner.run(self.dtest) + + def repr_failure(self, excinfo): + import doctest + if excinfo.errisinstance((doctest.DocTestFailure, + doctest.UnexpectedException)): + doctestfailure = excinfo.value + example = doctestfailure.example + test = doctestfailure.test + filename = test.filename + if test.lineno is None: + lineno = None + else: + lineno = test.lineno + example.lineno + 1 + message = excinfo.type.__name__ + reprlocation = ReprFileLocation(filename, lineno, message) + checker = _get_checker() + report_choice = _get_report_choice(self.config.getoption("doctestreport")) + if lineno is not None: + lines = doctestfailure.test.docstring.splitlines(False) + # add line numbers to the left of the error message + lines = ["%03d %s" % (i + test.lineno + 1, x) + for (i, x) in enumerate(lines)] + # trim docstring error lines to 10 + lines = lines[example.lineno - 9:example.lineno + 1] + else: + lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example'] + indent = '>>>' + for line in example.source.splitlines(): + lines.append('??? %s %s' % (indent, line)) + indent = '...' + if excinfo.errisinstance(doctest.DocTestFailure): + lines += checker.output_difference(example, + doctestfailure.got, report_choice).split("\n") + else: + inner_excinfo = ExceptionInfo(excinfo.value.exc_info) + lines += ["UNEXPECTED EXCEPTION: %s" % + repr(inner_excinfo.value)] + lines += traceback.format_exception(*excinfo.value.exc_info) + return ReprFailDoctest(reprlocation, lines) + else: + return super(DoctestItem, self).repr_failure(excinfo) + + def reportinfo(self): + return self.fspath, None, "[doctest] %s" % self.name + + +def _get_flag_lookup(): + import doctest + return dict(DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1, + DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE, + NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE, + ELLIPSIS=doctest.ELLIPSIS, + IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL, + COMPARISON_FLAGS=doctest.COMPARISON_FLAGS, + ALLOW_UNICODE=_get_allow_unicode_flag(), + ALLOW_BYTES=_get_allow_bytes_flag(), + ) + + +def get_optionflags(parent): + optionflags_str = parent.config.getini("doctest_optionflags") + flag_lookup_table = _get_flag_lookup() + flag_acc = 0 + for flag in optionflags_str: + flag_acc |= flag_lookup_table[flag] + return flag_acc + +class DoctestTextfile(pytest.Module): + obj = None + + def collect(self): + import doctest + + # inspired by doctest.testfile; ideally we would use it directly, + # but it doesn't support passing a custom checker + encoding = self.config.getini("doctest_encoding") + text = self.fspath.read_text(encoding) + filename = str(self.fspath) + name = self.fspath.basename + globs = {'__name__': '__main__'} + + optionflags = get_optionflags(self) + runner = doctest.DebugRunner(verbose=0, optionflags=optionflags, + checker=_get_checker()) + _fix_spoof_python2(runner, encoding) + + parser = doctest.DocTestParser() + test = parser.get_doctest(text, globs, name, filename, 0) + if test.examples: + yield DoctestItem(test.name, self, runner, test) + + +def _check_all_skipped(test): + """raises pytest.skip() if all examples in the given DocTest have the SKIP + option set. + """ + import doctest + all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) + if all_skipped: + pytest.skip('all tests skipped by +SKIP option') + + +class DoctestModule(pytest.Module): + def collect(self): + import doctest + if self.fspath.basename == "conftest.py": + module = self.config.pluginmanager._importconftest(self.fspath) + else: + try: + module = self.fspath.pyimport() + except ImportError: + if self.config.getvalue('doctest_ignore_import_errors'): + pytest.skip('unable to import module %r' % self.fspath) + else: + raise + # uses internal doctest module parsing mechanism + finder = doctest.DocTestFinder() + optionflags = get_optionflags(self) + runner = doctest.DebugRunner(verbose=0, optionflags=optionflags, + checker=_get_checker()) + + for test in finder.find(module, module.__name__): + if test.examples: # skip empty doctests + yield DoctestItem(test.name, self, runner, test) + + +def _setup_fixtures(doctest_item): + """ + Used by DoctestTextfile and DoctestItem to setup fixture information. + """ + def func(): + pass + + doctest_item.funcargs = {} + fm = doctest_item.session._fixturemanager + doctest_item._fixtureinfo = fm.getfixtureinfo(node=doctest_item, func=func, + cls=None, funcargs=False) + fixture_request = FixtureRequest(doctest_item) + fixture_request._fillfixtures() + return fixture_request + + +def _get_checker(): + """ + Returns a doctest.OutputChecker subclass that takes in account the + ALLOW_UNICODE option to ignore u'' prefixes in strings and ALLOW_BYTES + to strip b'' prefixes. + Useful when the same doctest should run in Python 2 and Python 3. + + An inner class is used to avoid importing "doctest" at the module + level. + """ + if hasattr(_get_checker, 'LiteralsOutputChecker'): + return _get_checker.LiteralsOutputChecker() + + import doctest + import re + + class LiteralsOutputChecker(doctest.OutputChecker): + """ + Copied from doctest_nose_plugin.py from the nltk project: + https://github.com/nltk/nltk + + Further extended to also support byte literals. + """ + + _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE) + _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE) + + def check_output(self, want, got, optionflags): + res = doctest.OutputChecker.check_output(self, want, got, + optionflags) + if res: + return True + + allow_unicode = optionflags & _get_allow_unicode_flag() + allow_bytes = optionflags & _get_allow_bytes_flag() + if not allow_unicode and not allow_bytes: + return False + + else: # pragma: no cover + def remove_prefixes(regex, txt): + return re.sub(regex, r'\1\2', txt) + + if allow_unicode: + want = remove_prefixes(self._unicode_literal_re, want) + got = remove_prefixes(self._unicode_literal_re, got) + if allow_bytes: + want = remove_prefixes(self._bytes_literal_re, want) + got = remove_prefixes(self._bytes_literal_re, got) + res = doctest.OutputChecker.check_output(self, want, got, + optionflags) + return res + + _get_checker.LiteralsOutputChecker = LiteralsOutputChecker + return _get_checker.LiteralsOutputChecker() + + +def _get_allow_unicode_flag(): + """ + Registers and returns the ALLOW_UNICODE flag. + """ + import doctest + return doctest.register_optionflag('ALLOW_UNICODE') + + +def _get_allow_bytes_flag(): + """ + Registers and returns the ALLOW_BYTES flag. + """ + import doctest + return doctest.register_optionflag('ALLOW_BYTES') + + +def _get_report_choice(key): + """ + This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid + importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests. + """ + import doctest + + return { + DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF, + DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE, + DOCTEST_REPORT_CHOICE_NONE: 0, + }[key] + + +def _fix_spoof_python2(runner, encoding): + """ + Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output. This + should patch only doctests for text files because they don't have a way to declare their + encoding. Doctests in docstrings from Python modules don't have the same problem given that + Python already decoded the strings. + + This fixes the problem related in issue #2434. + """ + from _pytest.compat import _PY2 + if not _PY2: + return + + from doctest import _SpoofOut + + class UnicodeSpoof(_SpoofOut): + + def getvalue(self): + result = _SpoofOut.getvalue(self) + if encoding: + result = result.decode(encoding) + return result + + runner._fakeout = UnicodeSpoof() + + +@pytest.fixture(scope='session') +def doctest_namespace(): + """ + Inject names into the doctest namespace. + """ + return dict() diff --git a/venv/lib/python3.5/site-packages/_pytest/fixtures.py b/venv/lib/python3.5/site-packages/_pytest/fixtures.py new file mode 100644 index 0000000..64d21b9 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/fixtures.py @@ -0,0 +1,1129 @@ +from __future__ import absolute_import, division, print_function +import sys + +from py._code.code import FormattedExcinfo + +import py +import warnings + +import inspect +import _pytest +from _pytest._code.code import TerminalRepr +from _pytest.compat import ( + NOTSET, exc_clear, _format_args, + getfslineno, get_real_func, + is_generator, isclass, getimfunc, + getlocation, getfuncargnames, + safe_getattr, +) +from _pytest.runner import fail +from _pytest.compat import FuncargnamesCompatAttr + +def pytest_sessionstart(session): + import _pytest.python + scopename2class.update({ + 'class': _pytest.python.Class, + 'module': _pytest.python.Module, + 'function': _pytest.main.Item, + }) + session._fixturemanager = FixtureManager(session) + + +scopename2class = {} + + +scope2props = dict(session=()) +scope2props["module"] = ("fspath", "module") +scope2props["class"] = scope2props["module"] + ("cls",) +scope2props["instance"] = scope2props["class"] + ("instance", ) +scope2props["function"] = scope2props["instance"] + ("function", "keywords") + +def scopeproperty(name=None, doc=None): + def decoratescope(func): + scopename = name or func.__name__ + + def provide(self): + if func.__name__ in scope2props[self.scope]: + return func(self) + raise AttributeError("%s not available in %s-scoped context" % ( + scopename, self.scope)) + + return property(provide, None, None, func.__doc__) + return decoratescope + + +def get_scope_node(node, scope): + cls = scopename2class.get(scope) + if cls is None: + if scope == "session": + return node.session + raise ValueError("unknown scope") + return node.getparent(cls) + + +def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager): + # this function will transform all collected calls to a functions + # if they use direct funcargs (i.e. direct parametrization) + # because we want later test execution to be able to rely on + # an existing FixtureDef structure for all arguments. + # XXX we can probably avoid this algorithm if we modify CallSpec2 + # to directly care for creating the fixturedefs within its methods. + if not metafunc._calls[0].funcargs: + return # this function call does not have direct parametrization + # collect funcargs of all callspecs into a list of values + arg2params = {} + arg2scope = {} + for callspec in metafunc._calls: + for argname, argvalue in callspec.funcargs.items(): + assert argname not in callspec.params + callspec.params[argname] = argvalue + arg2params_list = arg2params.setdefault(argname, []) + callspec.indices[argname] = len(arg2params_list) + arg2params_list.append(argvalue) + if argname not in arg2scope: + scopenum = callspec._arg2scopenum.get(argname, + scopenum_function) + arg2scope[argname] = scopes[scopenum] + callspec.funcargs.clear() + + # register artificial FixtureDef's so that later at test execution + # time we can rely on a proper FixtureDef to exist for fixture setup. + arg2fixturedefs = metafunc._arg2fixturedefs + for argname, valuelist in arg2params.items(): + # if we have a scope that is higher than function we need + # to make sure we only ever create an according fixturedef on + # a per-scope basis. We thus store and cache the fixturedef on the + # node related to the scope. + scope = arg2scope[argname] + node = None + if scope != "function": + node = get_scope_node(collector, scope) + if node is None: + assert scope == "class" and isinstance(collector, _pytest.python.Module) + # use module-level collector for class-scope (for now) + node = collector + if node and argname in node._name2pseudofixturedef: + arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]] + else: + fixturedef = FixtureDef(fixturemanager, '', argname, + get_direct_param_fixture_func, + arg2scope[argname], + valuelist, False, False) + arg2fixturedefs[argname] = [fixturedef] + if node is not None: + node._name2pseudofixturedef[argname] = fixturedef + + + +def getfixturemarker(obj): + """ return fixturemarker or None if it doesn't exist or raised + exceptions.""" + try: + return getattr(obj, "_pytestfixturefunction", None) + except Exception: + # some objects raise errors like request (from flask import request) + # we don't expect them to be fixture functions + return None + + + +def get_parametrized_fixture_keys(item, scopenum): + """ return list of keys for all parametrized arguments which match + the specified scope. """ + assert scopenum < scopenum_function # function + try: + cs = item.callspec + except AttributeError: + pass + else: + # cs.indictes.items() is random order of argnames but + # then again different functions (items) can change order of + # arguments so it doesn't matter much probably + for argname, param_index in cs.indices.items(): + if cs._arg2scopenum[argname] != scopenum: + continue + if scopenum == 0: # session + key = (argname, param_index) + elif scopenum == 1: # module + key = (argname, param_index, item.fspath) + elif scopenum == 2: # class + key = (argname, param_index, item.fspath, item.cls) + yield key + + +# algorithm for sorting on a per-parametrized resource setup basis +# it is called for scopenum==0 (session) first and performs sorting +# down to the lower scopes such as to minimize number of "high scope" +# setups and teardowns + +def reorder_items(items): + argkeys_cache = {} + for scopenum in range(0, scopenum_function): + argkeys_cache[scopenum] = d = {} + for item in items: + keys = set(get_parametrized_fixture_keys(item, scopenum)) + if keys: + d[item] = keys + return reorder_items_atscope(items, set(), argkeys_cache, 0) + +def reorder_items_atscope(items, ignore, argkeys_cache, scopenum): + if scopenum >= scopenum_function or len(items) < 3: + return items + items_done = [] + while 1: + items_before, items_same, items_other, newignore = \ + slice_items(items, ignore, argkeys_cache[scopenum]) + items_before = reorder_items_atscope( + items_before, ignore, argkeys_cache,scopenum+1) + if items_same is None: + # nothing to reorder in this scope + assert items_other is None + return items_done + items_before + items_done.extend(items_before) + items = items_same + items_other + ignore = newignore + + +def slice_items(items, ignore, scoped_argkeys_cache): + # we pick the first item which uses a fixture instance in the + # requested scope and which we haven't seen yet. We slice the input + # items list into a list of items_nomatch, items_same and + # items_other + if scoped_argkeys_cache: # do we need to do work at all? + it = iter(items) + # first find a slicing key + for i, item in enumerate(it): + argkeys = scoped_argkeys_cache.get(item) + if argkeys is not None: + argkeys = argkeys.difference(ignore) + if argkeys: # found a slicing key + slicing_argkey = argkeys.pop() + items_before = items[:i] + items_same = [item] + items_other = [] + # now slice the remainder of the list + for item in it: + argkeys = scoped_argkeys_cache.get(item) + if argkeys and slicing_argkey in argkeys and \ + slicing_argkey not in ignore: + items_same.append(item) + else: + items_other.append(item) + newignore = ignore.copy() + newignore.add(slicing_argkey) + return (items_before, items_same, items_other, newignore) + return items, None, None, None + + +def fillfixtures(function): + """ fill missing funcargs for a test function. """ + try: + request = function._request + except AttributeError: + # XXX this special code path is only expected to execute + # with the oejskit plugin. It uses classes with funcargs + # and we thus have to work a bit to allow this. + fm = function.session._fixturemanager + fi = fm.getfixtureinfo(function.parent, function.obj, None) + function._fixtureinfo = fi + request = function._request = FixtureRequest(function) + request._fillfixtures() + # prune out funcargs for jstests + newfuncargs = {} + for name in fi.argnames: + newfuncargs[name] = function.funcargs[name] + function.funcargs = newfuncargs + else: + request._fillfixtures() + + + +def get_direct_param_fixture_func(request): + return request.param + +class FuncFixtureInfo: + def __init__(self, argnames, names_closure, name2fixturedefs): + self.argnames = argnames + self.names_closure = names_closure + self.name2fixturedefs = name2fixturedefs + + +class FixtureRequest(FuncargnamesCompatAttr): + """ A request for a fixture from a test or fixture function. + + A request object gives access to the requesting test context + and has an optional ``param`` attribute in case + the fixture is parametrized indirectly. + """ + + def __init__(self, pyfuncitem): + self._pyfuncitem = pyfuncitem + #: fixture for which this request is being performed + self.fixturename = None + #: Scope string, one of "function", "class", "module", "session" + self.scope = "function" + self._fixture_values = {} # argname -> fixture value + self._fixture_defs = {} # argname -> FixtureDef + fixtureinfo = pyfuncitem._fixtureinfo + self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() + self._arg2index = {} + self._fixturemanager = pyfuncitem.session._fixturemanager + + @property + def fixturenames(self): + # backward incompatible note: now a readonly property + return list(self._pyfuncitem._fixtureinfo.names_closure) + + @property + def node(self): + """ underlying collection node (depends on current request scope)""" + return self._getscopeitem(self.scope) + + + def _getnextfixturedef(self, argname): + fixturedefs = self._arg2fixturedefs.get(argname, None) + if fixturedefs is None: + # we arrive here because of a a dynamic call to + # getfixturevalue(argname) usage which was naturally + # not known at parsing/collection time + parentid = self._pyfuncitem.parent.nodeid + fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid) + self._arg2fixturedefs[argname] = fixturedefs + # fixturedefs list is immutable so we maintain a decreasing index + index = self._arg2index.get(argname, 0) - 1 + if fixturedefs is None or (-index > len(fixturedefs)): + raise FixtureLookupError(argname, self) + self._arg2index[argname] = index + return fixturedefs[index] + + @property + def config(self): + """ the pytest config object associated with this request. """ + return self._pyfuncitem.config + + + @scopeproperty() + def function(self): + """ test function object if the request has a per-function scope. """ + return self._pyfuncitem.obj + + @scopeproperty("class") + def cls(self): + """ class (can be None) where the test function was collected. """ + clscol = self._pyfuncitem.getparent(_pytest.python.Class) + if clscol: + return clscol.obj + + @property + def instance(self): + """ instance (can be None) on which test function was collected. """ + # unittest support hack, see _pytest.unittest.TestCaseFunction + try: + return self._pyfuncitem._testcase + except AttributeError: + function = getattr(self, "function", None) + if function is not None: + return py.builtin._getimself(function) + + @scopeproperty() + def module(self): + """ python module object where the test function was collected. """ + return self._pyfuncitem.getparent(_pytest.python.Module).obj + + @scopeproperty() + def fspath(self): + """ the file system path of the test module which collected this test. """ + return self._pyfuncitem.fspath + + @property + def keywords(self): + """ keywords/markers dictionary for the underlying node. """ + return self.node.keywords + + @property + def session(self): + """ pytest session object. """ + return self._pyfuncitem.session + + def addfinalizer(self, finalizer): + """ add finalizer/teardown function to be called after the + last test within the requesting test context finished + execution. """ + # XXX usually this method is shadowed by fixturedef specific ones + self._addfinalizer(finalizer, scope=self.scope) + + def _addfinalizer(self, finalizer, scope): + colitem = self._getscopeitem(scope) + self._pyfuncitem.session._setupstate.addfinalizer( + finalizer=finalizer, colitem=colitem) + + def applymarker(self, marker): + """ Apply a marker to a single test function invocation. + This method is useful if you don't want to have a keyword/marker + on all function invocations. + + :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object + created by a call to ``pytest.mark.NAME(...)``. + """ + try: + self.node.keywords[marker.markname] = marker + except AttributeError: + raise ValueError(marker) + + def raiseerror(self, msg): + """ raise a FixtureLookupError with the given message. """ + raise self._fixturemanager.FixtureLookupError(None, self, msg) + + def _fillfixtures(self): + item = self._pyfuncitem + fixturenames = getattr(item, "fixturenames", self.fixturenames) + for argname in fixturenames: + if argname not in item.funcargs: + item.funcargs[argname] = self.getfixturevalue(argname) + + def cached_setup(self, setup, teardown=None, scope="module", extrakey=None): + """ (deprecated) Return a testing resource managed by ``setup`` & + ``teardown`` calls. ``scope`` and ``extrakey`` determine when the + ``teardown`` function will be called so that subsequent calls to + ``setup`` would recreate the resource. With pytest-2.3 you often + do not need ``cached_setup()`` as you can directly declare a scope + on a fixture function and register a finalizer through + ``request.addfinalizer()``. + + :arg teardown: function receiving a previously setup resource. + :arg setup: a no-argument function creating a resource. + :arg scope: a string value out of ``function``, ``class``, ``module`` + or ``session`` indicating the caching lifecycle of the resource. + :arg extrakey: added to internal caching key of (funcargname, scope). + """ + if not hasattr(self.config, '_setupcache'): + self.config._setupcache = {} # XXX weakref? + cachekey = (self.fixturename, self._getscopeitem(scope), extrakey) + cache = self.config._setupcache + try: + val = cache[cachekey] + except KeyError: + self._check_scope(self.fixturename, self.scope, scope) + val = setup() + cache[cachekey] = val + if teardown is not None: + def finalizer(): + del cache[cachekey] + teardown(val) + self._addfinalizer(finalizer, scope=scope) + return val + + def getfixturevalue(self, argname): + """ Dynamically run a named fixture function. + + Declaring fixtures via function argument is recommended where possible. + But if you can only decide whether to use another fixture at test + setup time, you may use this function to retrieve it inside a fixture + or test function body. + """ + return self._get_active_fixturedef(argname).cached_result[0] + + def getfuncargvalue(self, argname): + """ Deprecated, use getfixturevalue. """ + from _pytest import deprecated + warnings.warn( + deprecated.GETFUNCARGVALUE, + DeprecationWarning) + return self.getfixturevalue(argname) + + def _get_active_fixturedef(self, argname): + try: + return self._fixture_defs[argname] + except KeyError: + try: + fixturedef = self._getnextfixturedef(argname) + except FixtureLookupError: + if argname == "request": + class PseudoFixtureDef: + cached_result = (self, [0], None) + scope = "function" + return PseudoFixtureDef + raise + # remove indent to prevent the python3 exception + # from leaking into the call + result = self._getfixturevalue(fixturedef) + self._fixture_values[argname] = result + self._fixture_defs[argname] = fixturedef + return fixturedef + + def _get_fixturestack(self): + current = self + l = [] + while 1: + fixturedef = getattr(current, "_fixturedef", None) + if fixturedef is None: + l.reverse() + return l + l.append(fixturedef) + current = current._parent_request + + def _getfixturevalue(self, fixturedef): + # prepare a subrequest object before calling fixture function + # (latter managed by fixturedef) + argname = fixturedef.argname + funcitem = self._pyfuncitem + scope = fixturedef.scope + try: + param = funcitem.callspec.getparam(argname) + except (AttributeError, ValueError): + param = NOTSET + param_index = 0 + if fixturedef.params is not None: + frame = inspect.stack()[3] + frameinfo = inspect.getframeinfo(frame[0]) + source_path = frameinfo.filename + source_lineno = frameinfo.lineno + source_path = py.path.local(source_path) + if source_path.relto(funcitem.config.rootdir): + source_path = source_path.relto(funcitem.config.rootdir) + msg = ( + "The requested fixture has no parameter defined for the " + "current test.\n\nRequested fixture '{0}' defined in:\n{1}" + "\n\nRequested here:\n{2}:{3}".format( + fixturedef.argname, + getlocation(fixturedef.func, funcitem.config.rootdir), + source_path, + source_lineno, + ) + ) + fail(msg) + else: + # indices might not be set if old-style metafunc.addcall() was used + param_index = funcitem.callspec.indices.get(argname, 0) + # if a parametrize invocation set a scope it will override + # the static scope defined with the fixture function + paramscopenum = funcitem.callspec._arg2scopenum.get(argname) + if paramscopenum is not None: + scope = scopes[paramscopenum] + + subrequest = SubRequest(self, scope, param, param_index, fixturedef) + + # check if a higher-level scoped fixture accesses a lower level one + subrequest._check_scope(argname, self.scope, scope) + + # clear sys.exc_info before invoking the fixture (python bug?) + # if its not explicitly cleared it will leak into the call + exc_clear() + try: + # call the fixture function + val = fixturedef.execute(request=subrequest) + finally: + # if fixture function failed it might have registered finalizers + self.session._setupstate.addfinalizer(fixturedef.finish, + subrequest.node) + return val + + def _check_scope(self, argname, invoking_scope, requested_scope): + if argname == "request": + return + if scopemismatch(invoking_scope, requested_scope): + # try to report something helpful + lines = self._factorytraceback() + fail("ScopeMismatch: You tried to access the %r scoped " + "fixture %r with a %r scoped request object, " + "involved factories\n%s" % ( + (requested_scope, argname, invoking_scope, "\n".join(lines))), + pytrace=False) + + def _factorytraceback(self): + lines = [] + for fixturedef in self._get_fixturestack(): + factory = fixturedef.func + fs, lineno = getfslineno(factory) + p = self._pyfuncitem.session.fspath.bestrelpath(fs) + args = _format_args(factory) + lines.append("%s:%d: def %s%s" % ( + p, lineno, factory.__name__, args)) + return lines + + def _getscopeitem(self, scope): + if scope == "function": + # this might also be a non-function Item despite its attribute name + return self._pyfuncitem + node = get_scope_node(self._pyfuncitem, scope) + if node is None and scope == "class": + # fallback to function item itself + node = self._pyfuncitem + assert node + return node + + def __repr__(self): + return "" %(self.node) + + +class SubRequest(FixtureRequest): + """ a sub request for handling getting a fixture from a + test function/fixture. """ + def __init__(self, request, scope, param, param_index, fixturedef): + self._parent_request = request + self.fixturename = fixturedef.argname + if param is not NOTSET: + self.param = param + self.param_index = param_index + self.scope = scope + self._fixturedef = fixturedef + self.addfinalizer = fixturedef.addfinalizer + self._pyfuncitem = request._pyfuncitem + self._fixture_values = request._fixture_values + self._fixture_defs = request._fixture_defs + self._arg2fixturedefs = request._arg2fixturedefs + self._arg2index = request._arg2index + self._fixturemanager = request._fixturemanager + + def __repr__(self): + return "" % (self.fixturename, self._pyfuncitem) + + +class ScopeMismatchError(Exception): + """ A fixture function tries to use a different fixture function which + which has a lower scope (e.g. a Session one calls a function one) + """ + + +scopes = "session module class function".split() +scopenum_function = scopes.index("function") + + +def scopemismatch(currentscope, newscope): + return scopes.index(newscope) > scopes.index(currentscope) + + +def scope2index(scope, descr, where=None): + """Look up the index of ``scope`` and raise a descriptive value error + if not defined. + """ + try: + return scopes.index(scope) + except ValueError: + raise ValueError( + "{0} {1}has an unsupported scope value '{2}'".format( + descr, 'from {0} '.format(where) if where else '', + scope) + ) + + +class FixtureLookupError(LookupError): + """ could not return a requested Fixture (missing or invalid). """ + def __init__(self, argname, request, msg=None): + self.argname = argname + self.request = request + self.fixturestack = request._get_fixturestack() + self.msg = msg + + def formatrepr(self): + tblines = [] + addline = tblines.append + stack = [self.request._pyfuncitem.obj] + stack.extend(map(lambda x: x.func, self.fixturestack)) + msg = self.msg + if msg is not None: + # the last fixture raise an error, let's present + # it at the requesting side + stack = stack[:-1] + for function in stack: + fspath, lineno = getfslineno(function) + try: + lines, _ = inspect.getsourcelines(get_real_func(function)) + except (IOError, IndexError, TypeError): + error_msg = "file %s, line %s: source code not available" + addline(error_msg % (fspath, lineno+1)) + else: + addline("file %s, line %s" % (fspath, lineno+1)) + for i, line in enumerate(lines): + line = line.rstrip() + addline(" " + line) + if line.lstrip().startswith('def'): + break + + if msg is None: + fm = self.request._fixturemanager + available = [] + parentid = self.request._pyfuncitem.parent.nodeid + for name, fixturedefs in fm._arg2fixturedefs.items(): + faclist = list(fm._matchfactories(fixturedefs, parentid)) + if faclist and name not in available: + available.append(name) + msg = "fixture %r not found" % (self.argname,) + msg += "\n available fixtures: %s" %(", ".join(sorted(available)),) + msg += "\n use 'pytest --fixtures [testpath]' for help on them." + + return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) + + +class FixtureLookupErrorRepr(TerminalRepr): + def __init__(self, filename, firstlineno, tblines, errorstring, argname): + self.tblines = tblines + self.errorstring = errorstring + self.filename = filename + self.firstlineno = firstlineno + self.argname = argname + + def toterminal(self, tw): + # tw.line("FixtureLookupError: %s" %(self.argname), red=True) + for tbline in self.tblines: + tw.line(tbline.rstrip()) + lines = self.errorstring.split("\n") + if lines: + tw.line('{0} {1}'.format(FormattedExcinfo.fail_marker, + lines[0].strip()), red=True) + for line in lines[1:]: + tw.line('{0} {1}'.format(FormattedExcinfo.flow_marker, + line.strip()), red=True) + tw.line() + tw.line("%s:%d" % (self.filename, self.firstlineno+1)) + + +def fail_fixturefunc(fixturefunc, msg): + fs, lineno = getfslineno(fixturefunc) + location = "%s:%s" % (fs, lineno+1) + source = _pytest._code.Source(fixturefunc) + fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, + pytrace=False) + + +def call_fixture_func(fixturefunc, request, kwargs): + yieldctx = is_generator(fixturefunc) + if yieldctx: + it = fixturefunc(**kwargs) + res = next(it) + + def teardown(): + try: + next(it) + except StopIteration: + pass + else: + fail_fixturefunc(fixturefunc, + "yield_fixture function has more than one 'yield'") + + request.addfinalizer(teardown) + else: + res = fixturefunc(**kwargs) + return res + + +class FixtureDef: + """ A container for a factory definition. """ + def __init__(self, fixturemanager, baseid, argname, func, scope, params, + unittest=False, ids=None): + self._fixturemanager = fixturemanager + self.baseid = baseid or '' + self.has_location = baseid is not None + self.func = func + self.argname = argname + self.scope = scope + self.scopenum = scope2index( + scope or "function", + descr='fixture {0}'.format(func.__name__), + where=baseid + ) + self.params = params + startindex = unittest and 1 or None + self.argnames = getfuncargnames(func, startindex=startindex) + self.unittest = unittest + self.ids = ids + self._finalizer = [] + + def addfinalizer(self, finalizer): + self._finalizer.append(finalizer) + + def finish(self): + exceptions = [] + try: + while self._finalizer: + try: + func = self._finalizer.pop() + func() + except: + exceptions.append(sys.exc_info()) + if exceptions: + e = exceptions[0] + del exceptions # ensure we don't keep all frames alive because of the traceback + py.builtin._reraise(*e) + + finally: + ihook = self._fixturemanager.session.ihook + ihook.pytest_fixture_post_finalizer(fixturedef=self) + # even if finalization fails, we invalidate + # the cached fixture value + if hasattr(self, "cached_result"): + del self.cached_result + + def execute(self, request): + # get required arguments and register our own finish() + # with their finalization + for argname in self.argnames: + fixturedef = request._get_active_fixturedef(argname) + if argname != "request": + fixturedef.addfinalizer(self.finish) + + my_cache_key = request.param_index + cached_result = getattr(self, "cached_result", None) + if cached_result is not None: + result, cache_key, err = cached_result + if my_cache_key == cache_key: + if err is not None: + py.builtin._reraise(*err) + else: + return result + # we have a previous but differently parametrized fixture instance + # so we need to tear it down before creating a new one + self.finish() + assert not hasattr(self, "cached_result") + + ihook = self._fixturemanager.session.ihook + return ihook.pytest_fixture_setup(fixturedef=self, request=request) + + def __repr__(self): + return ("" % + (self.argname, self.scope, self.baseid)) + +def pytest_fixture_setup(fixturedef, request): + """ Execution of fixture setup. """ + kwargs = {} + for argname in fixturedef.argnames: + fixdef = request._get_active_fixturedef(argname) + result, arg_cache_key, exc = fixdef.cached_result + request._check_scope(argname, request.scope, fixdef.scope) + kwargs[argname] = result + + fixturefunc = fixturedef.func + if fixturedef.unittest: + if request.instance is not None: + # bind the unbound method to the TestCase instance + fixturefunc = fixturedef.func.__get__(request.instance) + else: + # the fixture function needs to be bound to the actual + # request.instance so that code working with "fixturedef" behaves + # as expected. + if request.instance is not None: + fixturefunc = getimfunc(fixturedef.func) + if fixturefunc != fixturedef.func: + fixturefunc = fixturefunc.__get__(request.instance) + my_cache_key = request.param_index + try: + result = call_fixture_func(fixturefunc, request, kwargs) + except Exception: + fixturedef.cached_result = (None, my_cache_key, sys.exc_info()) + raise + fixturedef.cached_result = (result, my_cache_key, None) + return result + + +class FixtureFunctionMarker: + def __init__(self, scope, params, autouse=False, ids=None, name=None): + self.scope = scope + self.params = params + self.autouse = autouse + self.ids = ids + self.name = name + + def __call__(self, function): + if isclass(function): + raise ValueError( + "class fixtures not supported (may be in the future)") + function._pytestfixturefunction = self + return function + + + +def fixture(scope="function", params=None, autouse=False, ids=None, name=None): + """ (return a) decorator to mark a fixture factory function. + + This decorator can be used (with or without parameters) to define a + fixture function. The name of the fixture function can later be + referenced to cause its invocation ahead of running tests: test + modules or classes can use the pytest.mark.usefixtures(fixturename) + marker. Test functions can directly use fixture names as input + arguments in which case the fixture instance returned from the fixture + function will be injected. + + :arg scope: the scope for which this fixture is shared, one of + "function" (default), "class", "module" or "session". + + :arg params: an optional list of parameters which will cause multiple + invocations of the fixture function and all of the tests + using it. + + :arg autouse: if True, the fixture func is activated for all tests that + can see it. If False (the default) then an explicit + reference is needed to activate the fixture. + + :arg ids: list of string ids each corresponding to the params + so that they are part of the test id. If no ids are provided + they will be generated automatically from the params. + + :arg name: the name of the fixture. This defaults to the name of the + decorated function. If a fixture is used in the same module in + which it is defined, the function name of the fixture will be + shadowed by the function arg that requests the fixture; one way + to resolve this is to name the decorated function + ``fixture_`` and then use + ``@pytest.fixture(name='')``. + + Fixtures can optionally provide their values to test functions using a ``yield`` statement, + instead of ``return``. In this case, the code block after the ``yield`` statement is executed + as teardown code regardless of the test outcome. A fixture function must yield exactly once. + """ + if callable(scope) and params is None and autouse == False: + # direct decoration + return FixtureFunctionMarker( + "function", params, autouse, name=name)(scope) + if params is not None and not isinstance(params, (list, tuple)): + params = list(params) + return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name) + + +def yield_fixture(scope="function", params=None, autouse=False, ids=None, name=None): + """ (return a) decorator to mark a yield-fixture factory function. + + .. deprecated:: 3.0 + Use :py:func:`pytest.fixture` directly instead. + """ + if callable(scope) and params is None and not autouse: + # direct decoration + return FixtureFunctionMarker( + "function", params, autouse, ids=ids, name=name)(scope) + else: + return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name) + + +defaultfuncargprefixmarker = fixture() + + +@fixture(scope="session") +def pytestconfig(request): + """ the pytest config object with access to command line opts.""" + return request.config + + +class FixtureManager: + """ + pytest fixtures definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached per node/func-name. + This FuncFixtureInfo object is later retrieved by Function nodes + which themselves offer a fixturenames attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - ini-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i. e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup of their FuncFixtureInfo. + """ + + _argprefix = "pytest_funcarg__" + FixtureLookupError = FixtureLookupError + FixtureLookupErrorRepr = FixtureLookupErrorRepr + + def __init__(self, session): + self.session = session + self.config = session.config + self._arg2fixturedefs = {} + self._holderobjseen = set() + self._arg2finish = {} + self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] + session.config.pluginmanager.register(self, "funcmanage") + + + def getfixtureinfo(self, node, func, cls, funcargs=True): + if funcargs and not hasattr(node, "nofuncargs"): + if cls is not None: + startindex = 1 + else: + startindex = None + argnames = getfuncargnames(func, startindex) + else: + argnames = () + usefixtures = getattr(func, "usefixtures", None) + initialnames = argnames + if usefixtures is not None: + initialnames = usefixtures.args + initialnames + fm = node.session._fixturemanager + names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames, + node) + return FuncFixtureInfo(argnames, names_closure, arg2fixturedefs) + + def pytest_plugin_registered(self, plugin): + nodeid = None + try: + p = py.path.local(plugin.__file__) + except AttributeError: + pass + else: + # construct the base nodeid which is later used to check + # what fixtures are visible for particular tests (as denoted + # by their test id) + if p.basename.startswith("conftest.py"): + nodeid = p.dirpath().relto(self.config.rootdir) + if p.sep != "/": + nodeid = nodeid.replace(p.sep, "/") + self.parsefactories(plugin, nodeid) + + def _getautousenames(self, nodeid): + """ return a tuple of fixture names to be used. """ + autousenames = [] + for baseid, basenames in self._nodeid_and_autousenames: + if nodeid.startswith(baseid): + if baseid: + i = len(baseid) + nextchar = nodeid[i:i+1] + if nextchar and nextchar not in ":/": + continue + autousenames.extend(basenames) + # make sure autousenames are sorted by scope, scopenum 0 is session + autousenames.sort( + key=lambda x: self._arg2fixturedefs[x][-1].scopenum) + return autousenames + + def getfixtureclosure(self, fixturenames, parentnode): + # collect the closure of all fixtures , starting with the given + # fixturenames as the initial set. As we have to visit all + # factory definitions anyway, we also return a arg2fixturedefs + # mapping so that the caller can reuse it and does not have + # to re-discover fixturedefs again for each fixturename + # (discovering matching fixtures for a given name/node is expensive) + + parentid = parentnode.nodeid + fixturenames_closure = self._getautousenames(parentid) + + def merge(otherlist): + for arg in otherlist: + if arg not in fixturenames_closure: + fixturenames_closure.append(arg) + + merge(fixturenames) + arg2fixturedefs = {} + lastlen = -1 + while lastlen != len(fixturenames_closure): + lastlen = len(fixturenames_closure) + for argname in fixturenames_closure: + if argname in arg2fixturedefs: + continue + fixturedefs = self.getfixturedefs(argname, parentid) + if fixturedefs: + arg2fixturedefs[argname] = fixturedefs + merge(fixturedefs[-1].argnames) + return fixturenames_closure, arg2fixturedefs + + def pytest_generate_tests(self, metafunc): + for argname in metafunc.fixturenames: + faclist = metafunc._arg2fixturedefs.get(argname) + if faclist: + fixturedef = faclist[-1] + if fixturedef.params is not None: + func_params = getattr(getattr(metafunc.function, 'parametrize', None), 'args', [[None]]) + # skip directly parametrized arguments + argnames = func_params[0] + if not isinstance(argnames, (tuple, list)): + argnames = [x.strip() for x in argnames.split(",") if x.strip()] + if argname not in func_params and argname not in argnames: + metafunc.parametrize(argname, fixturedef.params, + indirect=True, scope=fixturedef.scope, + ids=fixturedef.ids) + else: + continue # will raise FixtureLookupError at setup time + + def pytest_collection_modifyitems(self, items): + # separate parametrized setups + items[:] = reorder_items(items) + + def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False): + if nodeid is not NOTSET: + holderobj = node_or_obj + else: + holderobj = node_or_obj.obj + nodeid = node_or_obj.nodeid + if holderobj in self._holderobjseen: + return + self._holderobjseen.add(holderobj) + autousenames = [] + for name in dir(holderobj): + # The attribute can be an arbitrary descriptor, so the attribute + # access below can raise. safe_getatt() ignores such exceptions. + obj = safe_getattr(holderobj, name, None) + # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) + # or are "@pytest.fixture" marked + marker = getfixturemarker(obj) + if marker is None: + if not name.startswith(self._argprefix): + continue + if not callable(obj): + continue + marker = defaultfuncargprefixmarker + from _pytest import deprecated + self.config.warn('C1', deprecated.FUNCARG_PREFIX.format(name=name), nodeid=nodeid) + name = name[len(self._argprefix):] + elif not isinstance(marker, FixtureFunctionMarker): + # magic globals with __getattr__ might have got us a wrong + # fixture attribute + continue + else: + if marker.name: + name = marker.name + msg = 'fixtures cannot have "pytest_funcarg__" prefix ' \ + 'and be decorated with @pytest.fixture:\n%s' % name + assert not name.startswith(self._argprefix), msg + + fixture_def = FixtureDef(self, nodeid, name, obj, + marker.scope, marker.params, + unittest=unittest, ids=marker.ids) + + faclist = self._arg2fixturedefs.setdefault(name, []) + if fixture_def.has_location: + faclist.append(fixture_def) + else: + # fixturedefs with no location are at the front + # so this inserts the current fixturedef after the + # existing fixturedefs from external plugins but + # before the fixturedefs provided in conftests. + i = len([f for f in faclist if not f.has_location]) + faclist.insert(i, fixture_def) + if marker.autouse: + autousenames.append(name) + + if autousenames: + self._nodeid_and_autousenames.append((nodeid or '', autousenames)) + + def getfixturedefs(self, argname, nodeid): + """ + Gets a list of fixtures which are applicable to the given node id. + + :param str argname: name of the fixture to search for + :param str nodeid: full node id of the requesting test. + :return: list[FixtureDef] + """ + try: + fixturedefs = self._arg2fixturedefs[argname] + except KeyError: + return None + else: + return tuple(self._matchfactories(fixturedefs, nodeid)) + + def _matchfactories(self, fixturedefs, nodeid): + for fixturedef in fixturedefs: + if nodeid.startswith(fixturedef.baseid): + yield fixturedef + diff --git a/venv/lib/python3.5/site-packages/_pytest/freeze_support.py b/venv/lib/python3.5/site-packages/_pytest/freeze_support.py new file mode 100644 index 0000000..52f8608 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/freeze_support.py @@ -0,0 +1,44 @@ +""" +Provides a function to report all internal modules for using freezing tools +pytest +""" +from __future__ import absolute_import, division, print_function + + + +def freeze_includes(): + """ + Returns a list of module names used by py.test that should be + included by cx_freeze. + """ + import py + import _pytest + result = list(_iter_all_modules(py)) + result += list(_iter_all_modules(_pytest)) + return result + + +def _iter_all_modules(package, prefix=''): + """ + Iterates over the names of all modules that can be found in the given + package, recursively. + Example: + _iter_all_modules(_pytest) -> + ['_pytest.assertion.newinterpret', + '_pytest.capture', + '_pytest.core', + ... + ] + """ + import os + import pkgutil + if type(package) is not str: + path, prefix = package.__path__[0], package.__name__ + '.' + else: + path = package + for _, name, is_package in pkgutil.iter_modules([path]): + if is_package: + for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'): + yield prefix + m + else: + yield prefix + name diff --git a/venv/lib/python3.5/site-packages/_pytest/helpconfig.py b/venv/lib/python3.5/site-packages/_pytest/helpconfig.py new file mode 100644 index 0000000..e3c6b6e --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/helpconfig.py @@ -0,0 +1,179 @@ +""" version info, help messages, tracing configuration. """ +from __future__ import absolute_import, division, print_function + +import py +import pytest +from _pytest.config import PrintHelp +import os, sys +from argparse import Action + + +class HelpAction(Action): + """This is an argparse Action that will raise an exception in + order to skip the rest of the argument parsing when --help is passed. + This prevents argparse from quitting due to missing required arguments + when any are defined, for example by ``pytest_addoption``. + This is similar to the way that the builtin argparse --help option is + implemented by raising SystemExit. + """ + + def __init__(self, + option_strings, + dest=None, + default=False, + help=None): + super(HelpAction, self).__init__( + option_strings=option_strings, + dest=dest, + const=True, + default=default, + nargs=0, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, self.const) + + # We should only skip the rest of the parsing after preparse is done + if getattr(parser._parser, 'after_preparse', False): + raise PrintHelp + + +def pytest_addoption(parser): + group = parser.getgroup('debugconfig') + group.addoption('--version', action="store_true", + help="display pytest lib version and import information.") + group._addoption("-h", "--help", action=HelpAction, dest="help", + help="show help message and configuration info") + group._addoption('-p', action="append", dest="plugins", default = [], + metavar="name", + help="early-load given plugin (multi-allowed). " + "To avoid loading of plugins, use the `no:` prefix, e.g. " + "`no:doctest`.") + group.addoption('--traceconfig', '--trace-config', + action="store_true", default=False, + help="trace considerations of conftest.py files."), + group.addoption('--debug', + action="store_true", dest="debug", default=False, + help="store internal tracing debug information in 'pytestdebug.log'.") + group._addoption( + '-o', '--override-ini', nargs='*', dest="override_ini", + action="append", + help="override config option with option=value style, e.g. `-o xfail_strict=True`.") + + +@pytest.hookimpl(hookwrapper=True) +def pytest_cmdline_parse(): + outcome = yield + config = outcome.get_result() + if config.option.debug: + path = os.path.abspath("pytestdebug.log") + debugfile = open(path, 'w') + debugfile.write("versions pytest-%s, py-%s, " + "python-%s\ncwd=%s\nargs=%s\n\n" %( + pytest.__version__, py.__version__, + ".".join(map(str, sys.version_info)), + os.getcwd(), config._origargs)) + config.trace.root.setwriter(debugfile.write) + undo_tracing = config.pluginmanager.enable_tracing() + sys.stderr.write("writing pytestdebug information to %s\n" % path) + + def unset_tracing(): + debugfile.close() + sys.stderr.write("wrote pytestdebug information to %s\n" % + debugfile.name) + config.trace.root.setwriter(None) + undo_tracing() + + config.add_cleanup(unset_tracing) + +def pytest_cmdline_main(config): + if config.option.version: + p = py.path.local(pytest.__file__) + sys.stderr.write("This is pytest version %s, imported from %s\n" % + (pytest.__version__, p)) + plugininfo = getpluginversioninfo(config) + if plugininfo: + for line in plugininfo: + sys.stderr.write(line + "\n") + return 0 + elif config.option.help: + config._do_configure() + showhelp(config) + config._ensure_unconfigure() + return 0 + +def showhelp(config): + reporter = config.pluginmanager.get_plugin('terminalreporter') + tw = reporter._tw + tw.write(config._parser.optparser.format_help()) + tw.line() + tw.line() + tw.line("[pytest] ini-options in the first " + "pytest.ini|tox.ini|setup.cfg file found:") + tw.line() + + for name in config._parser._ininames: + help, type, default = config._parser._inidict[name] + if type is None: + type = "string" + spec = "%s (%s)" % (name, type) + line = " %-24s %s" %(spec, help) + tw.line(line[:tw.fullwidth]) + + tw.line() + tw.line("environment variables:") + vars = [ + ("PYTEST_ADDOPTS", "extra command line options"), + ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"), + ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals") + ] + for name, help in vars: + tw.line(" %-24s %s" % (name, help)) + tw.line() + tw.line() + + tw.line("to see available markers type: pytest --markers") + tw.line("to see available fixtures type: pytest --fixtures") + tw.line("(shown according to specified file_or_dir or current dir " + "if not specified)") + + for warningreport in reporter.stats.get('warnings', []): + tw.line("warning : " + warningreport.message, red=True) + return + + +conftest_options = [ + ('pytest_plugins', 'list of plugin names to load'), +] + +def getpluginversioninfo(config): + lines = [] + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + lines.append("setuptools registered plugins:") + for plugin, dist in plugininfo: + loc = getattr(plugin, '__file__', repr(plugin)) + content = "%s-%s at %s" % (dist.project_name, dist.version, loc) + lines.append(" " + content) + return lines + +def pytest_report_header(config): + lines = [] + if config.option.debug or config.option.traceconfig: + lines.append("using: pytest-%s pylib-%s" % + (pytest.__version__,py.__version__)) + + verinfo = getpluginversioninfo(config) + if verinfo: + lines.extend(verinfo) + + if config.option.traceconfig: + lines.append("active plugins:") + items = config.pluginmanager.list_name_plugin() + for name, plugin in items: + if hasattr(plugin, '__file__'): + r = plugin.__file__ + else: + r = repr(plugin) + lines.append(" %-20s: %s" %(name, r)) + return lines diff --git a/venv/lib/python3.5/site-packages/_pytest/hookspec.py b/venv/lib/python3.5/site-packages/_pytest/hookspec.py new file mode 100644 index 0000000..2c9a661 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/hookspec.py @@ -0,0 +1,353 @@ +""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """ + +from _pytest._pluggy import HookspecMarker + +hookspec = HookspecMarker("pytest") + +# ------------------------------------------------------------------------- +# Initialization hooks called for every plugin +# ------------------------------------------------------------------------- + +@hookspec(historic=True) +def pytest_addhooks(pluginmanager): + """called at plugin registration time to allow adding new hooks via a call to + pluginmanager.add_hookspecs(module_or_class, prefix).""" + + +@hookspec(historic=True) +def pytest_namespace(): + """ + DEPRECATED: this hook causes direct monkeypatching on pytest, its use is strongly discouraged + return dict of name->object to be made globally available in + the pytest namespace. This hook is called at plugin registration + time. + """ + +@hookspec(historic=True) +def pytest_plugin_registered(plugin, manager): + """ a new pytest plugin got registered. """ + + +@hookspec(historic=True) +def pytest_addoption(parser): + """register argparse-style options and ini-style config values, + called once at the beginning of a test run. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + + :arg parser: To add command line options, call + :py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`. + To add ini-file values call :py:func:`parser.addini(...) + <_pytest.config.Parser.addini>`. + + Options can later be accessed through the + :py:class:`config <_pytest.config.Config>` object, respectively: + + - :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to + retrieve the value of a command line option. + + - :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve + a value read from an ini-style file. + + The config object is passed around on many internal objects via the ``.config`` + attribute or can be retrieved as the ``pytestconfig`` fixture or accessed + via (deprecated) ``pytest.config``. + """ + +@hookspec(historic=True) +def pytest_configure(config): + """ called after command line options have been parsed + and all plugins and initial conftest files been loaded. + This hook is called for every plugin. + """ + +# ------------------------------------------------------------------------- +# Bootstrapping hooks called for plugins registered early enough: +# internal and 3rd party plugins as well as directly +# discoverable conftest.py local plugins. +# ------------------------------------------------------------------------- + +@hookspec(firstresult=True) +def pytest_cmdline_parse(pluginmanager, args): + """return initialized config object, parsing the specified args. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_cmdline_preparse(config, args): + """(deprecated) modify command line arguments before option parsing. """ + +@hookspec(firstresult=True) +def pytest_cmdline_main(config): + """ called for performing the main command line action. The default + implementation will invoke the configure hooks and runtest_mainloop. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_load_initial_conftests(early_config, parser, args): + """ implements the loading of initial conftest files ahead + of command line option parsing. """ + + +# ------------------------------------------------------------------------- +# collection hooks +# ------------------------------------------------------------------------- + +@hookspec(firstresult=True) +def pytest_collection(session): + """ perform the collection protocol for the given session. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_collection_modifyitems(session, config, items): + """ called after collection has been performed, may filter or re-order + the items in-place.""" + +def pytest_collection_finish(session): + """ called after collection has been performed and modified. """ + +@hookspec(firstresult=True) +def pytest_ignore_collect(path, config): + """ return True to prevent considering this path for collection. + This hook is consulted for all files and directories prior to calling + more specific hooks. + + Stops at first non-None result, see :ref:`firstresult` + """ + +@hookspec(firstresult=True) +def pytest_collect_directory(path, parent): + """ called before traversing a directory for collection files. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_collect_file(path, parent): + """ return collection Node or None for the given path. Any new node + needs to have the specified ``parent`` as a parent.""" + +# logging hooks for collection +def pytest_collectstart(collector): + """ collector starts collecting. """ + +def pytest_itemcollected(item): + """ we just collected a test item. """ + +def pytest_collectreport(report): + """ collector finished collecting. """ + +def pytest_deselected(items): + """ called for test items deselected by keyword. """ + +@hookspec(firstresult=True) +def pytest_make_collect_report(collector): + """ perform ``collector.collect()`` and return a CollectReport. + + Stops at first non-None result, see :ref:`firstresult` """ + +# ------------------------------------------------------------------------- +# Python test function related hooks +# ------------------------------------------------------------------------- + +@hookspec(firstresult=True) +def pytest_pycollect_makemodule(path, parent): + """ return a Module collector or None for the given path. + This hook will be called for each matching test module path. + The pytest_collect_file hook needs to be used if you want to + create test modules for files that do not match as a test module. + + Stops at first non-None result, see :ref:`firstresult` """ + +@hookspec(firstresult=True) +def pytest_pycollect_makeitem(collector, name, obj): + """ return custom item/collector for a python object in a module, or None. + + Stops at first non-None result, see :ref:`firstresult` """ + +@hookspec(firstresult=True) +def pytest_pyfunc_call(pyfuncitem): + """ call underlying test function. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_generate_tests(metafunc): + """ generate (multiple) parametrized calls to a test function.""" + +@hookspec(firstresult=True) +def pytest_make_parametrize_id(config, val, argname): + """Return a user-friendly string representation of the given ``val`` that will be used + by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``. + The parameter name is available as ``argname``, if required. + + Stops at first non-None result, see :ref:`firstresult` """ + +# ------------------------------------------------------------------------- +# generic runtest related hooks +# ------------------------------------------------------------------------- + +@hookspec(firstresult=True) +def pytest_runtestloop(session): + """ called for performing the main runtest loop + (after collection finished). + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_itemstart(item, node): + """ (deprecated, use pytest_runtest_logstart). """ + +@hookspec(firstresult=True) +def pytest_runtest_protocol(item, nextitem): + """ implements the runtest_setup/call/teardown protocol for + the given test item, including capturing exceptions and calling + reporting hooks. + + :arg item: test item for which the runtest protocol is performed. + + :arg nextitem: the scheduled-to-be-next test item (or None if this + is the end my friend). This argument is passed on to + :py:func:`pytest_runtest_teardown`. + + :return boolean: True if no further hook implementations should be invoked. + + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_runtest_logstart(nodeid, location): + """ signal the start of running a single test item. """ + +def pytest_runtest_setup(item): + """ called before ``pytest_runtest_call(item)``. """ + +def pytest_runtest_call(item): + """ called to execute the test ``item``. """ + +def pytest_runtest_teardown(item, nextitem): + """ called after ``pytest_runtest_call``. + + :arg nextitem: the scheduled-to-be-next test item (None if no further + test item is scheduled). This argument can be used to + perform exact teardowns, i.e. calling just enough finalizers + so that nextitem only needs to call setup-functions. + """ + +@hookspec(firstresult=True) +def pytest_runtest_makereport(item, call): + """ return a :py:class:`_pytest.runner.TestReport` object + for the given :py:class:`pytest.Item <_pytest.main.Item>` and + :py:class:`_pytest.runner.CallInfo`. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_runtest_logreport(report): + """ process a test setup/call/teardown report relating to + the respective phase of executing a test. """ + +# ------------------------------------------------------------------------- +# Fixture related hooks +# ------------------------------------------------------------------------- + +@hookspec(firstresult=True) +def pytest_fixture_setup(fixturedef, request): + """ performs fixture setup execution. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_fixture_post_finalizer(fixturedef): + """ called after fixture teardown, but before the cache is cleared so + the fixture result cache ``fixturedef.cached_result`` can + still be accessed.""" + +# ------------------------------------------------------------------------- +# test session related hooks +# ------------------------------------------------------------------------- + +def pytest_sessionstart(session): + """ before session.main() is called. """ + +def pytest_sessionfinish(session, exitstatus): + """ whole test run finishes. """ + +def pytest_unconfigure(config): + """ called before test process is exited. """ + + +# ------------------------------------------------------------------------- +# hooks for customizing the assert methods +# ------------------------------------------------------------------------- + +def pytest_assertrepr_compare(config, op, left, right): + """return explanation for comparisons in failing assert expressions. + + Return None for no custom explanation, otherwise return a list + of strings. The strings will be joined by newlines but any newlines + *in* a string will be escaped. Note that all but the first line will + be indented slightly, the intention is for the first line to be a summary. + """ + +# ------------------------------------------------------------------------- +# hooks for influencing reporting (invoked from _pytest_terminal) +# ------------------------------------------------------------------------- + +def pytest_report_header(config, startdir): + """ return a string to be displayed as header info for terminal reporting. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + """ + +@hookspec(firstresult=True) +def pytest_report_teststatus(report): + """ return result-category, shortletter and verbose word for reporting. + + Stops at first non-None result, see :ref:`firstresult` """ + +def pytest_terminal_summary(terminalreporter, exitstatus): + """ add additional section in terminal summary reporting. """ + + +@hookspec(historic=True) +def pytest_logwarning(message, code, nodeid, fslocation): + """ process a warning specified by a message, a code string, + a nodeid and fslocation (both of which may be None + if the warning is not tied to a partilar node/location).""" + +# ------------------------------------------------------------------------- +# doctest hooks +# ------------------------------------------------------------------------- + +@hookspec(firstresult=True) +def pytest_doctest_prepare_content(content): + """ return processed content for a given doctest + + Stops at first non-None result, see :ref:`firstresult` """ + +# ------------------------------------------------------------------------- +# error handling and internal debugging hooks +# ------------------------------------------------------------------------- + +def pytest_internalerror(excrepr, excinfo): + """ called for internal errors. """ + +def pytest_keyboard_interrupt(excinfo): + """ called for keyboard interrupt. """ + +def pytest_exception_interact(node, call, report): + """called when an exception was raised which can potentially be + interactively handled. + + This hook is only called if an exception was raised + that is not an internal exception like ``skip.Exception``. + """ + +def pytest_enter_pdb(config): + """ called upon pdb.set_trace(), can be used by plugins to take special + action just before the python debugger enters in interactive mode. + + :arg config: pytest config object + :type config: _pytest.config.Config + """ diff --git a/venv/lib/python3.5/site-packages/_pytest/junitxml.py b/venv/lib/python3.5/site-packages/_pytest/junitxml.py new file mode 100644 index 0000000..3016337 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/junitxml.py @@ -0,0 +1,452 @@ +""" + report test results in JUnit-XML format, + for use with Jenkins and build integration servers. + + +Based on initial code from Ross Lawley. + +Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/ +src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd +""" +from __future__ import absolute_import, division, print_function + +import functools +import py +import os +import re +import sys +import time +import pytest +from _pytest.config import filename_arg + +# Python 2.X and 3.X compatibility +if sys.version_info[0] < 3: + from codecs import open +else: + unichr = chr + unicode = str + long = int + + +class Junit(py.xml.Namespace): + pass + + +# We need to get the subset of the invalid unicode ranges according to +# XML 1.0 which are valid in this python build. Hence we calculate +# this dynamically instead of hardcoding it. The spec range of valid +# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] +# | [#x10000-#x10FFFF] +_legal_chars = (0x09, 0x0A, 0x0d) +_legal_ranges = ( + (0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF), +) +_legal_xml_re = [ + unicode("%s-%s") % (unichr(low), unichr(high)) + for (low, high) in _legal_ranges if low < sys.maxunicode +] +_legal_xml_re = [unichr(x) for x in _legal_chars] + _legal_xml_re +illegal_xml_re = re.compile(unicode('[^%s]') % unicode('').join(_legal_xml_re)) +del _legal_chars +del _legal_ranges +del _legal_xml_re + +_py_ext_re = re.compile(r"\.py$") + + +def bin_xml_escape(arg): + def repl(matchobj): + i = ord(matchobj.group()) + if i <= 0xFF: + return unicode('#x%02X') % i + else: + return unicode('#x%04X') % i + + return py.xml.raw(illegal_xml_re.sub(repl, py.xml.escape(arg))) + + +class _NodeReporter(object): + def __init__(self, nodeid, xml): + + self.id = nodeid + self.xml = xml + self.add_stats = self.xml.add_stats + self.duration = 0 + self.properties = [] + self.nodes = [] + self.testcase = None + self.attrs = {} + + def append(self, node): + self.xml.add_stats(type(node).__name__) + self.nodes.append(node) + + def add_property(self, name, value): + self.properties.append((str(name), bin_xml_escape(value))) + + def make_properties_node(self): + """Return a Junit node containing custom properties, if any. + """ + if self.properties: + return Junit.properties([ + Junit.property(name=name, value=value) + for name, value in self.properties + ]) + return '' + + def record_testreport(self, testreport): + assert not self.testcase + names = mangle_test_address(testreport.nodeid) + classnames = names[:-1] + if self.xml.prefix: + classnames.insert(0, self.xml.prefix) + attrs = { + "classname": ".".join(classnames), + "name": bin_xml_escape(names[-1]), + "file": testreport.location[0], + } + if testreport.location[1] is not None: + attrs["line"] = testreport.location[1] + if hasattr(testreport, "url"): + attrs["url"] = testreport.url + self.attrs = attrs + + def to_xml(self): + testcase = Junit.testcase(time=self.duration, **self.attrs) + testcase.append(self.make_properties_node()) + for node in self.nodes: + testcase.append(node) + return testcase + + def _add_simple(self, kind, message, data=None): + data = bin_xml_escape(data) + node = kind(data, message=message) + self.append(node) + + def write_captured_output(self, report): + for capname in ('out', 'err'): + content = getattr(report, 'capstd' + capname) + if content: + tag = getattr(Junit, 'system-' + capname) + self.append(tag(bin_xml_escape(content))) + + def append_pass(self, report): + self.add_stats('passed') + + def append_failure(self, report): + # msg = str(report.longrepr.reprtraceback.extraline) + if hasattr(report, "wasxfail"): + self._add_simple( + Junit.skipped, + "xfail-marked test passes unexpectedly") + else: + if hasattr(report.longrepr, "reprcrash"): + message = report.longrepr.reprcrash.message + elif isinstance(report.longrepr, (unicode, str)): + message = report.longrepr + else: + message = str(report.longrepr) + message = bin_xml_escape(message) + fail = Junit.failure(message=message) + fail.append(bin_xml_escape(report.longrepr)) + self.append(fail) + + def append_collect_error(self, report): + # msg = str(report.longrepr.reprtraceback.extraline) + self.append(Junit.error(bin_xml_escape(report.longrepr), + message="collection failure")) + + def append_collect_skipped(self, report): + self._add_simple( + Junit.skipped, "collection skipped", report.longrepr) + + def append_error(self, report): + if getattr(report, 'when', None) == 'teardown': + msg = "test teardown failure" + else: + msg = "test setup failure" + self._add_simple( + Junit.error, msg, report.longrepr) + + def append_skipped(self, report): + if hasattr(report, "wasxfail"): + self._add_simple( + Junit.skipped, "expected test failure", report.wasxfail + ) + else: + filename, lineno, skipreason = report.longrepr + if skipreason.startswith("Skipped: "): + skipreason = bin_xml_escape(skipreason[9:]) + self.append( + Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason), + type="pytest.skip", + message=skipreason)) + self.write_captured_output(report) + + def finalize(self): + data = self.to_xml().unicode(indent=0) + self.__dict__.clear() + self.to_xml = lambda: py.xml.raw(data) + + +@pytest.fixture +def record_xml_property(request): + """Add extra xml properties to the tag for the calling test. + The fixture is callable with ``(name, value)``, with value being automatically + xml-encoded. + """ + request.node.warn( + code='C3', + message='record_xml_property is an experimental feature', + ) + xml = getattr(request.config, "_xml", None) + if xml is not None: + node_reporter = xml.node_reporter(request.node.nodeid) + return node_reporter.add_property + else: + def add_property_noop(name, value): + pass + + return add_property_noop + + +def pytest_addoption(parser): + group = parser.getgroup("terminal reporting") + group.addoption( + '--junitxml', '--junit-xml', + action="store", + dest="xmlpath", + metavar="path", + type=functools.partial(filename_arg, optname="--junitxml"), + default=None, + help="create junit-xml style report file at given path.") + group.addoption( + '--junitprefix', '--junit-prefix', + action="store", + metavar="str", + default=None, + help="prepend prefix to classnames in junit-xml output") + parser.addini("junit_suite_name", "Test suite name for JUnit report", default="pytest") + + +def pytest_configure(config): + xmlpath = config.option.xmlpath + # prevent opening xmllog on slave nodes (xdist) + if xmlpath and not hasattr(config, 'slaveinput'): + config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini("junit_suite_name")) + config.pluginmanager.register(config._xml) + + +def pytest_unconfigure(config): + xml = getattr(config, '_xml', None) + if xml: + del config._xml + config.pluginmanager.unregister(xml) + + +def mangle_test_address(address): + path, possible_open_bracket, params = address.partition('[') + names = path.split("::") + try: + names.remove('()') + except ValueError: + pass + # convert file path to dotted path + names[0] = names[0].replace("/", '.') + names[0] = _py_ext_re.sub("", names[0]) + # put any params back + names[-1] += possible_open_bracket + params + return names + + +class LogXML(object): + def __init__(self, logfile, prefix, suite_name="pytest"): + logfile = os.path.expanduser(os.path.expandvars(logfile)) + self.logfile = os.path.normpath(os.path.abspath(logfile)) + self.prefix = prefix + self.suite_name = suite_name + self.stats = dict.fromkeys([ + 'error', + 'passed', + 'failure', + 'skipped', + ], 0) + self.node_reporters = {} # nodeid -> _NodeReporter + self.node_reporters_ordered = [] + self.global_properties = [] + # List of reports that failed on call but teardown is pending. + self.open_reports = [] + self.cnt_double_fail_tests = 0 + + def finalize(self, report): + nodeid = getattr(report, 'nodeid', report) + # local hack to handle xdist report order + slavenode = getattr(report, 'node', None) + reporter = self.node_reporters.pop((nodeid, slavenode)) + if reporter is not None: + reporter.finalize() + + def node_reporter(self, report): + nodeid = getattr(report, 'nodeid', report) + # local hack to handle xdist report order + slavenode = getattr(report, 'node', None) + + key = nodeid, slavenode + + if key in self.node_reporters: + # TODO: breasks for --dist=each + return self.node_reporters[key] + + reporter = _NodeReporter(nodeid, self) + + self.node_reporters[key] = reporter + self.node_reporters_ordered.append(reporter) + + return reporter + + def add_stats(self, key): + if key in self.stats: + self.stats[key] += 1 + + def _opentestcase(self, report): + reporter = self.node_reporter(report) + reporter.record_testreport(report) + return reporter + + def pytest_runtest_logreport(self, report): + """handle a setup/call/teardown report, generating the appropriate + xml tags as necessary. + + note: due to plugins like xdist, this hook may be called in interlaced + order with reports from other nodes. for example: + + usual call order: + -> setup node1 + -> call node1 + -> teardown node1 + -> setup node2 + -> call node2 + -> teardown node2 + + possible call order in xdist: + -> setup node1 + -> call node1 + -> setup node2 + -> call node2 + -> teardown node2 + -> teardown node1 + """ + close_report = None + if report.passed: + if report.when == "call": # ignore setup/teardown + reporter = self._opentestcase(report) + reporter.append_pass(report) + elif report.failed: + if report.when == "teardown": + # The following vars are needed when xdist plugin is used + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + (rep for rep in self.open_reports + if (rep.nodeid == report.nodeid and + getattr(rep, "item_index", None) == report_ii and + getattr(rep, "worker_id", None) == report_wid + ) + ), None) + if close_report: + # We need to open new testcase in case we have failure in + # call and error in teardown in order to follow junit + # schema + self.finalize(close_report) + self.cnt_double_fail_tests += 1 + reporter = self._opentestcase(report) + if report.when == "call": + reporter.append_failure(report) + self.open_reports.append(report) + else: + reporter.append_error(report) + elif report.skipped: + reporter = self._opentestcase(report) + reporter.append_skipped(report) + self.update_testcase_duration(report) + if report.when == "teardown": + reporter = self._opentestcase(report) + reporter.write_captured_output(report) + self.finalize(report) + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + (rep for rep in self.open_reports + if (rep.nodeid == report.nodeid and + getattr(rep, "item_index", None) == report_ii and + getattr(rep, "worker_id", None) == report_wid + ) + ), None) + if close_report: + self.open_reports.remove(close_report) + + def update_testcase_duration(self, report): + """accumulates total duration for nodeid from given report and updates + the Junit.testcase with the new total if already created. + """ + reporter = self.node_reporter(report) + reporter.duration += getattr(report, 'duration', 0.0) + + def pytest_collectreport(self, report): + if not report.passed: + reporter = self._opentestcase(report) + if report.failed: + reporter.append_collect_error(report) + else: + reporter.append_collect_skipped(report) + + def pytest_internalerror(self, excrepr): + reporter = self.node_reporter('internal') + reporter.attrs.update(classname="pytest", name='internal') + reporter._add_simple(Junit.error, 'internal error', excrepr) + + def pytest_sessionstart(self): + self.suite_start_time = time.time() + + def pytest_sessionfinish(self): + dirname = os.path.dirname(os.path.abspath(self.logfile)) + if not os.path.isdir(dirname): + os.makedirs(dirname) + logfile = open(self.logfile, 'w', encoding='utf-8') + suite_stop_time = time.time() + suite_time_delta = suite_stop_time - self.suite_start_time + + numtests = (self.stats['passed'] + self.stats['failure'] + + self.stats['skipped'] + self.stats['error'] - + self.cnt_double_fail_tests) + logfile.write('') + + logfile.write(Junit.testsuite( + self._get_global_properties_node(), + [x.to_xml() for x in self.node_reporters_ordered], + name=self.suite_name, + errors=self.stats['error'], + failures=self.stats['failure'], + skips=self.stats['skipped'], + tests=numtests, + time="%.3f" % suite_time_delta, ).unicode(indent=0)) + logfile.close() + + def pytest_terminal_summary(self, terminalreporter): + terminalreporter.write_sep("-", + "generated xml file: %s" % (self.logfile)) + + def add_global_property(self, name, value): + self.global_properties.append((str(name), bin_xml_escape(value))) + + def _get_global_properties_node(self): + """Return a Junit node containing custom properties, if any. + """ + if self.global_properties: + return Junit.properties( + [ + Junit.property(name=name, value=value) + for name, value in self.global_properties + ] + ) + return '' diff --git a/venv/lib/python3.5/site-packages/_pytest/main.py b/venv/lib/python3.5/site-packages/_pytest/main.py new file mode 100644 index 0000000..e6f679a --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/main.py @@ -0,0 +1,784 @@ +""" core implementation of testing process: init, session, runtest loop. """ +from __future__ import absolute_import, division, print_function + +import functools +import os +import sys + +import _pytest +import _pytest._code +import py +try: + from collections import MutableMapping as MappingMixin +except ImportError: + from UserDict import DictMixin as MappingMixin + +from _pytest.config import directory_arg, UsageError, hookimpl +from _pytest.runner import collect_one_node, exit + +tracebackcutdir = py.path.local(_pytest.__file__).dirpath() + +# exitcodes for the command line +EXIT_OK = 0 +EXIT_TESTSFAILED = 1 +EXIT_INTERRUPTED = 2 +EXIT_INTERNALERROR = 3 +EXIT_USAGEERROR = 4 +EXIT_NOTESTSCOLLECTED = 5 + + +def pytest_addoption(parser): + parser.addini("norecursedirs", "directory patterns to avoid for recursion", + type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv']) + parser.addini("testpaths", "directories to search for tests when no files or directories are given in the command line.", + type="args", default=[]) + #parser.addini("dirpatterns", + # "patterns specifying possible locations of test files", + # type="linelist", default=["**/test_*.txt", + # "**/test_*.py", "**/*_test.py"] + #) + group = parser.getgroup("general", "running and selection options") + group._addoption('-x', '--exitfirst', action="store_const", + dest="maxfail", const=1, + help="exit instantly on first error or failed test."), + group._addoption('--maxfail', metavar="num", + action="store", type=int, dest="maxfail", default=0, + help="exit after first num failures or errors.") + group._addoption('--strict', action="store_true", + help="run pytest in strict mode, warnings become errors.") + group._addoption("-c", metavar="file", type=str, dest="inifilename", + help="load configuration from `file` instead of trying to locate one of the implicit configuration files.") + group._addoption("--continue-on-collection-errors", action="store_true", + default=False, dest="continue_on_collection_errors", + help="Force test execution even if collection errors occur.") + + group = parser.getgroup("collect", "collection") + group.addoption('--collectonly', '--collect-only', action="store_true", + help="only collect tests, don't execute them."), + group.addoption('--pyargs', action="store_true", + help="try to interpret all arguments as python packages.") + group.addoption("--ignore", action="append", metavar="path", + help="ignore path during collection (multi-allowed).") + # when changing this to --conf-cut-dir, config.py Conftest.setinitial + # needs upgrading as well + group.addoption('--confcutdir', dest="confcutdir", default=None, + metavar="dir", type=functools.partial(directory_arg, optname="--confcutdir"), + help="only load conftest.py's relative to specified dir.") + group.addoption('--noconftest', action="store_true", + dest="noconftest", default=False, + help="Don't load any conftest.py files.") + group.addoption('--keepduplicates', '--keep-duplicates', action="store_true", + dest="keepduplicates", default=False, + help="Keep duplicate tests.") + + group = parser.getgroup("debugconfig", + "test session debugging and configuration") + group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir", + help="base temporary directory for this test run.") + + + +def pytest_namespace(): + """keeping this one works around a deeper startup issue in pytest + + i tried to find it for a while but the amount of time turned unsustainable, + so i put a hack in to revisit later + """ + return {} + + +def pytest_configure(config): + __import__('pytest').config = config # compatibiltiy + + +def wrap_session(config, doit): + """Skeleton command line program""" + session = Session(config) + session.exitstatus = EXIT_OK + initstate = 0 + try: + try: + config._do_configure() + initstate = 1 + config.hook.pytest_sessionstart(session=session) + initstate = 2 + session.exitstatus = doit(config, session) or 0 + except UsageError: + raise + except KeyboardInterrupt: + excinfo = _pytest._code.ExceptionInfo() + if initstate < 2 and isinstance(excinfo.value, exit.Exception): + sys.stderr.write('{0}: {1}\n'.format( + excinfo.typename, excinfo.value.msg)) + config.hook.pytest_keyboard_interrupt(excinfo=excinfo) + session.exitstatus = EXIT_INTERRUPTED + except: + excinfo = _pytest._code.ExceptionInfo() + config.notify_exception(excinfo, config.option) + session.exitstatus = EXIT_INTERNALERROR + if excinfo.errisinstance(SystemExit): + sys.stderr.write("mainloop: caught Spurious SystemExit!\n") + + finally: + excinfo = None # Explicitly break reference cycle. + session.startdir.chdir() + if initstate >= 2: + config.hook.pytest_sessionfinish( + session=session, + exitstatus=session.exitstatus) + config._ensure_unconfigure() + return session.exitstatus + + +def pytest_cmdline_main(config): + return wrap_session(config, _main) + + +def _main(config, session): + """ default command line protocol for initialization, session, + running tests and reporting. """ + config.hook.pytest_collection(session=session) + config.hook.pytest_runtestloop(session=session) + + if session.testsfailed: + return EXIT_TESTSFAILED + elif session.testscollected == 0: + return EXIT_NOTESTSCOLLECTED + + +def pytest_collection(session): + return session.perform_collect() + + +def pytest_runtestloop(session): + if (session.testsfailed and + not session.config.option.continue_on_collection_errors): + raise session.Interrupted( + "%d errors during collection" % session.testsfailed) + + if session.config.option.collectonly: + return True + + for i, item in enumerate(session.items): + nextitem = session.items[i+1] if i+1 < len(session.items) else None + item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) + if session.shouldstop: + raise session.Interrupted(session.shouldstop) + return True + + +def pytest_ignore_collect(path, config): + ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath()) + ignore_paths = ignore_paths or [] + excludeopt = config.getoption("ignore") + if excludeopt: + ignore_paths.extend([py.path.local(x) for x in excludeopt]) + + if py.path.local(path) in ignore_paths: + return True + + # Skip duplicate paths. + keepduplicates = config.getoption("keepduplicates") + duplicate_paths = config.pluginmanager._duplicatepaths + if not keepduplicates: + if path in duplicate_paths: + return True + else: + duplicate_paths.add(path) + + return False + + +class FSHookProxy: + def __init__(self, fspath, pm, remove_mods): + self.fspath = fspath + self.pm = pm + self.remove_mods = remove_mods + + def __getattr__(self, name): + x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) + self.__dict__[name] = x + return x + +class _CompatProperty(object): + def __init__(self, name): + self.name = name + + def __get__(self, obj, owner): + if obj is None: + return self + + # TODO: reenable in the features branch + # warnings.warn( + # "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format( + # name=self.name, owner=type(owner).__name__), + # PendingDeprecationWarning, stacklevel=2) + return getattr(__import__('pytest'), self.name) + + + +class NodeKeywords(MappingMixin): + def __init__(self, node): + self.node = node + self.parent = node.parent + self._markers = {node.name: True} + + def __getitem__(self, key): + try: + return self._markers[key] + except KeyError: + if self.parent is None: + raise + return self.parent.keywords[key] + + def __setitem__(self, key, value): + self._markers[key] = value + + def __delitem__(self, key): + raise ValueError("cannot delete key in keywords dict") + + def __iter__(self): + seen = set(self._markers) + if self.parent is not None: + seen.update(self.parent.keywords) + return iter(seen) + + def __len__(self): + return len(self.__iter__()) + + def keys(self): + return list(self) + + def __repr__(self): + return "" % (self.node, ) + + +class Node(object): + """ base class for Collector and Item the test collection tree. + Collector subclasses have children, Items are terminal nodes.""" + + def __init__(self, name, parent=None, config=None, session=None): + #: a unique name within the scope of the parent node + self.name = name + + #: the parent collector node. + self.parent = parent + + #: the pytest config object + self.config = config or parent.config + + #: the session this node is part of + self.session = session or parent.session + + #: filesystem path where this node was collected from (can be None) + self.fspath = getattr(parent, 'fspath', None) + + #: keywords/markers collected from all scopes + self.keywords = NodeKeywords(self) + + #: allow adding of extra keywords to use for matching + self.extra_keyword_matches = set() + + # used for storing artificial fixturedefs for direct parametrization + self._name2pseudofixturedef = {} + + @property + def ihook(self): + """ fspath sensitive hook proxy used to call pytest hooks""" + return self.session.gethookproxy(self.fspath) + + Module = _CompatProperty("Module") + Class = _CompatProperty("Class") + Instance = _CompatProperty("Instance") + Function = _CompatProperty("Function") + File = _CompatProperty("File") + Item = _CompatProperty("Item") + + def _getcustomclass(self, name): + maybe_compatprop = getattr(type(self), name) + if isinstance(maybe_compatprop, _CompatProperty): + return getattr(__import__('pytest'), name) + else: + cls = getattr(self, name) + # TODO: reenable in the features branch + # warnings.warn("use of node.%s is deprecated, " + # "use pytest_pycollect_makeitem(...) to create custom " + # "collection nodes" % name, category=DeprecationWarning) + return cls + + def __repr__(self): + return "<%s %r>" %(self.__class__.__name__, + getattr(self, 'name', None)) + + def warn(self, code, message): + """ generate a warning with the given code and message for this + item. """ + assert isinstance(code, str) + fslocation = getattr(self, "location", None) + if fslocation is None: + fslocation = getattr(self, "fspath", None) + self.ihook.pytest_logwarning.call_historic(kwargs=dict( + code=code, message=message, + nodeid=self.nodeid, fslocation=fslocation)) + + # methods for ordering nodes + @property + def nodeid(self): + """ a ::-separated string denoting its collection tree address. """ + try: + return self._nodeid + except AttributeError: + self._nodeid = x = self._makeid() + return x + + def _makeid(self): + return self.parent.nodeid + "::" + self.name + + def __hash__(self): + return hash(self.nodeid) + + def setup(self): + pass + + def teardown(self): + pass + + def _memoizedcall(self, attrname, function): + exattrname = "_ex_" + attrname + failure = getattr(self, exattrname, None) + if failure is not None: + py.builtin._reraise(failure[0], failure[1], failure[2]) + if hasattr(self, attrname): + return getattr(self, attrname) + try: + res = function() + except py.builtin._sysex: + raise + except: + failure = sys.exc_info() + setattr(self, exattrname, failure) + raise + setattr(self, attrname, res) + return res + + def listchain(self): + """ return list of all parent collectors up to self, + starting from root of collection tree. """ + chain = [] + item = self + while item is not None: + chain.append(item) + item = item.parent + chain.reverse() + return chain + + def add_marker(self, marker): + """ dynamically add a marker object to the node. + + ``marker`` can be a string or pytest.mark.* instance. + """ + from _pytest.mark import MarkDecorator, MARK_GEN + if isinstance(marker, py.builtin._basestring): + marker = getattr(MARK_GEN, marker) + elif not isinstance(marker, MarkDecorator): + raise ValueError("is not a string or pytest.mark.* Marker") + self.keywords[marker.name] = marker + + def get_marker(self, name): + """ get a marker object from this node or None if + the node doesn't have a marker with that name. """ + val = self.keywords.get(name, None) + if val is not None: + from _pytest.mark import MarkInfo, MarkDecorator + if isinstance(val, (MarkDecorator, MarkInfo)): + return val + + def listextrakeywords(self): + """ Return a set of all extra keywords in self and any parents.""" + extra_keywords = set() + item = self + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) + return extra_keywords + + def listnames(self): + return [x.name for x in self.listchain()] + + def addfinalizer(self, fin): + """ register a function to be called when this node is finalized. + + This method can only be called when this node is active + in a setup chain, for example during self.setup(). + """ + self.session._setupstate.addfinalizer(fin, self) + + def getparent(self, cls): + """ get the next parent node (including ourself) + which is an instance of the given class""" + current = self + while current and not isinstance(current, cls): + current = current.parent + return current + + def _prunetraceback(self, excinfo): + pass + + def _repr_failure_py(self, excinfo, style=None): + fm = self.session._fixturemanager + if excinfo.errisinstance(fm.FixtureLookupError): + return excinfo.value.formatrepr() + tbfilter = True + if self.config.option.fulltrace: + style="long" + else: + tb = _pytest._code.Traceback([excinfo.traceback[-1]]) + self._prunetraceback(excinfo) + if len(excinfo.traceback) == 0: + excinfo.traceback = tb + tbfilter = False # prunetraceback already does it + if style == "auto": + style = "long" + # XXX should excinfo.getrepr record all data and toterminal() process it? + if style is None: + if self.config.option.tbstyle == "short": + style = "short" + else: + style = "long" + + try: + os.getcwd() + abspath = False + except OSError: + abspath = True + + return excinfo.getrepr(funcargs=True, abspath=abspath, + showlocals=self.config.option.showlocals, + style=style, tbfilter=tbfilter) + + repr_failure = _repr_failure_py + +class Collector(Node): + """ Collector instances create children through collect() + and thus iteratively build a tree. + """ + + class CollectError(Exception): + """ an error during collection, contains a custom message. """ + + def collect(self): + """ returns a list of children (items and collectors) + for this collection node. + """ + raise NotImplementedError("abstract") + + def repr_failure(self, excinfo): + """ represent a collection failure. """ + if excinfo.errisinstance(self.CollectError): + exc = excinfo.value + return str(exc.args[0]) + return self._repr_failure_py(excinfo, style="short") + + def _prunetraceback(self, excinfo): + if hasattr(self, 'fspath'): + traceback = excinfo.traceback + ntraceback = traceback.cut(path=self.fspath) + if ntraceback == traceback: + ntraceback = ntraceback.cut(excludepath=tracebackcutdir) + excinfo.traceback = ntraceback.filter() + +class FSCollector(Collector): + def __init__(self, fspath, parent=None, config=None, session=None): + fspath = py.path.local(fspath) # xxx only for test_resultlog.py? + name = fspath.basename + if parent is not None: + rel = fspath.relto(parent.fspath) + if rel: + name = rel + name = name.replace(os.sep, "/") + super(FSCollector, self).__init__(name, parent, config, session) + self.fspath = fspath + + def _makeid(self): + relpath = self.fspath.relto(self.config.rootdir) + if os.sep != "/": + relpath = relpath.replace(os.sep, "/") + return relpath + +class File(FSCollector): + """ base class for collecting tests from a file. """ + +class Item(Node): + """ a basic test invocation item. Note that for a single function + there might be multiple test invocation items. + """ + nextitem = None + + def __init__(self, name, parent=None, config=None, session=None): + super(Item, self).__init__(name, parent, config, session) + self._report_sections = [] + + def add_report_section(self, when, key, content): + if content: + self._report_sections.append((when, key, content)) + + def reportinfo(self): + return self.fspath, None, "" + + @property + def location(self): + try: + return self._location + except AttributeError: + location = self.reportinfo() + # bestrelpath is a quite slow function + cache = self.config.__dict__.setdefault("_bestrelpathcache", {}) + try: + fspath = cache[location[0]] + except KeyError: + fspath = self.session.fspath.bestrelpath(location[0]) + cache[location[0]] = fspath + location = (fspath, location[1], str(location[2])) + self._location = location + return location + +class NoMatch(Exception): + """ raised if matching cannot locate a matching names. """ + +class Interrupted(KeyboardInterrupt): + """ signals an interrupted test run. """ + __module__ = 'builtins' # for py3 + +class Session(FSCollector): + Interrupted = Interrupted + + def __init__(self, config): + FSCollector.__init__(self, config.rootdir, parent=None, + config=config, session=self) + self.testsfailed = 0 + self.testscollected = 0 + self.shouldstop = False + self.trace = config.trace.root.get("collection") + self._norecursepatterns = config.getini("norecursedirs") + self.startdir = py.path.local() + self.config.pluginmanager.register(self, name="session") + + def _makeid(self): + return "" + + @hookimpl(tryfirst=True) + def pytest_collectstart(self): + if self.shouldstop: + raise self.Interrupted(self.shouldstop) + + @hookimpl(tryfirst=True) + def pytest_runtest_logreport(self, report): + if report.failed and not hasattr(report, 'wasxfail'): + self.testsfailed += 1 + maxfail = self.config.getvalue("maxfail") + if maxfail and self.testsfailed >= maxfail: + self.shouldstop = "stopping after %d failures" % ( + self.testsfailed) + pytest_collectreport = pytest_runtest_logreport + + def isinitpath(self, path): + return path in self._initialpaths + + def gethookproxy(self, fspath): + # check if we have the common case of running + # hooks with all conftest.py filesall conftest.py + pm = self.config.pluginmanager + my_conftestmodules = pm._getconftestmodules(fspath) + remove_mods = pm._conftest_plugins.difference(my_conftestmodules) + if remove_mods: + # one or more conftests are not in use at this fspath + proxy = FSHookProxy(fspath, pm, remove_mods) + else: + # all plugis are active for this fspath + proxy = self.config.hook + return proxy + + def perform_collect(self, args=None, genitems=True): + hook = self.config.hook + try: + items = self._perform_collect(args, genitems) + self.config.pluginmanager.check_pending() + hook.pytest_collection_modifyitems(session=self, + config=self.config, items=items) + finally: + hook.pytest_collection_finish(session=self) + self.testscollected = len(items) + return items + + def _perform_collect(self, args, genitems): + if args is None: + args = self.config.args + self.trace("perform_collect", self, args) + self.trace.root.indent += 1 + self._notfound = [] + self._initialpaths = set() + self._initialparts = [] + self.items = items = [] + for arg in args: + parts = self._parsearg(arg) + self._initialparts.append(parts) + self._initialpaths.add(parts[0]) + rep = collect_one_node(self) + self.ihook.pytest_collectreport(report=rep) + self.trace.root.indent -= 1 + if self._notfound: + errors = [] + for arg, exc in self._notfound: + line = "(no name %r in any of %r)" % (arg, exc.args[0]) + errors.append("not found: %s\n%s" % (arg, line)) + # XXX: test this + raise UsageError(*errors) + if not genitems: + return rep.result + else: + if rep.passed: + for node in rep.result: + self.items.extend(self.genitems(node)) + return items + + def collect(self): + for parts in self._initialparts: + arg = "::".join(map(str, parts)) + self.trace("processing argument", arg) + self.trace.root.indent += 1 + try: + for x in self._collect(arg): + yield x + except NoMatch: + # we are inside a make_report hook so + # we cannot directly pass through the exception + self._notfound.append((arg, sys.exc_info()[1])) + + self.trace.root.indent -= 1 + + def _collect(self, arg): + names = self._parsearg(arg) + path = names.pop(0) + if path.check(dir=1): + assert not names, "invalid arg %r" % (arg,) + for path in path.visit(fil=lambda x: x.check(file=1), + rec=self._recurse, bf=True, sort=True): + for x in self._collectfile(path): + yield x + else: + assert path.check(file=1) + for x in self.matchnodes(self._collectfile(path), names): + yield x + + def _collectfile(self, path): + ihook = self.gethookproxy(path) + if not self.isinitpath(path): + if ihook.pytest_ignore_collect(path=path, config=self.config): + return () + return ihook.pytest_collect_file(path=path, parent=self) + + def _recurse(self, path): + ihook = self.gethookproxy(path.dirpath()) + if ihook.pytest_ignore_collect(path=path, config=self.config): + return + for pat in self._norecursepatterns: + if path.check(fnmatch=pat): + return False + ihook = self.gethookproxy(path) + ihook.pytest_collect_directory(path=path, parent=self) + return True + + def _tryconvertpyarg(self, x): + """Convert a dotted module name to path. + + """ + import pkgutil + try: + loader = pkgutil.find_loader(x) + except ImportError: + return x + if loader is None: + return x + # This method is sometimes invoked when AssertionRewritingHook, which + # does not define a get_filename method, is already in place: + try: + path = loader.get_filename(x) + except AttributeError: + # Retrieve path from AssertionRewritingHook: + path = loader.modules[x][0].co_filename + if loader.is_package(x): + path = os.path.dirname(path) + return path + + def _parsearg(self, arg): + """ return (fspath, names) tuple after checking the file exists. """ + parts = str(arg).split("::") + if self.config.option.pyargs: + parts[0] = self._tryconvertpyarg(parts[0]) + relpath = parts[0].replace("/", os.sep) + path = self.config.invocation_dir.join(relpath, abs=True) + if not path.check(): + if self.config.option.pyargs: + raise UsageError( + "file or package not found: " + arg + + " (missing __init__.py?)") + else: + raise UsageError("file not found: " + arg) + parts[0] = path + return parts + + def matchnodes(self, matching, names): + self.trace("matchnodes", matching, names) + self.trace.root.indent += 1 + nodes = self._matchnodes(matching, names) + num = len(nodes) + self.trace("matchnodes finished -> ", num, "nodes") + self.trace.root.indent -= 1 + if num == 0: + raise NoMatch(matching, names[:1]) + return nodes + + def _matchnodes(self, matching, names): + if not matching or not names: + return matching + name = names[0] + assert name + nextnames = names[1:] + resultnodes = [] + for node in matching: + if isinstance(node, Item): + if not names: + resultnodes.append(node) + continue + assert isinstance(node, Collector) + rep = collect_one_node(node) + if rep.passed: + has_matched = False + for x in rep.result: + # TODO: remove parametrized workaround once collection structure contains parametrization + if x.name == name or x.name.split("[")[0] == name: + resultnodes.extend(self.matchnodes([x], nextnames)) + has_matched = True + # XXX accept IDs that don't have "()" for class instances + if not has_matched and len(rep.result) == 1 and x.name == "()": + nextnames.insert(0, name) + resultnodes.extend(self.matchnodes([x], nextnames)) + else: + # report collection failures here to avoid failing to run some test + # specified in the command line because the module could not be + # imported (#134) + node.ihook.pytest_collectreport(report=rep) + return resultnodes + + def genitems(self, node): + self.trace("genitems", node) + if isinstance(node, Item): + node.ihook.pytest_itemcollected(item=node) + yield node + else: + assert isinstance(node, Collector) + rep = collect_one_node(node) + if rep.passed: + for subnode in rep.result: + for x in self.genitems(subnode): + yield x + node.ihook.pytest_collectreport(report=rep) diff --git a/venv/lib/python3.5/site-packages/_pytest/mark.py b/venv/lib/python3.5/site-packages/_pytest/mark.py new file mode 100644 index 0000000..8b40a4f --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/mark.py @@ -0,0 +1,391 @@ +""" generic mechanism for marking and selecting python functions. """ +from __future__ import absolute_import, division, print_function + +import inspect +from collections import namedtuple +from operator import attrgetter +from .compat import imap + + +def alias(name): + return property(attrgetter(name), doc='alias for ' + name) + + +class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')): + @classmethod + def param(cls, *values, **kw): + marks = kw.pop('marks', ()) + if isinstance(marks, MarkDecorator): + marks = marks, + else: + assert isinstance(marks, (tuple, list, set)) + + def param_extract_id(id=None): + return id + + id = param_extract_id(**kw) + return cls(values, marks, id) + + @classmethod + def extract_from(cls, parameterset, legacy_force_tuple=False): + """ + :param parameterset: + a legacy style parameterset that may or may not be a tuple, + and may or may not be wrapped into a mess of mark objects + + :param legacy_force_tuple: + enforce tuple wrapping so single argument tuple values + don't get decomposed and break tests + + """ + + if isinstance(parameterset, cls): + return parameterset + if not isinstance(parameterset, MarkDecorator) and legacy_force_tuple: + return cls.param(parameterset) + + newmarks = [] + argval = parameterset + while isinstance(argval, MarkDecorator): + newmarks.append(MarkDecorator(Mark( + argval.markname, argval.args[:-1], argval.kwargs))) + argval = argval.args[-1] + assert not isinstance(argval, ParameterSet) + if legacy_force_tuple: + argval = argval, + + return cls(argval, marks=newmarks, id=None) + + @property + def deprecated_arg_dict(self): + return dict((mark.name, mark) for mark in self.marks) + + +class MarkerError(Exception): + + """Error in use of a pytest marker/attribute.""" + + +def param(*values, **kw): + return ParameterSet.param(*values, **kw) + + +def pytest_addoption(parser): + group = parser.getgroup("general") + group._addoption( + '-k', + action="store", dest="keyword", default='', metavar="EXPRESSION", + help="only run tests which match the given substring expression. " + "An expression is a python evaluatable expression " + "where all names are substring-matched against test names " + "and their parent classes. Example: -k 'test_method or test_" + "other' matches all test functions and classes whose name " + "contains 'test_method' or 'test_other'. " + "Additionally keywords are matched to classes and functions " + "containing extra names in their 'extra_keyword_matches' set, " + "as well as functions which have names assigned directly to them." + ) + + group._addoption( + "-m", + action="store", dest="markexpr", default="", metavar="MARKEXPR", + help="only run tests matching given mark expression. " + "example: -m 'mark1 and not mark2'." + ) + + group.addoption( + "--markers", action="store_true", + help="show markers (builtin, plugin and per-project ones)." + ) + + parser.addini("markers", "markers for test functions", 'linelist') + + +def pytest_cmdline_main(config): + import _pytest.config + if config.option.markers: + config._do_configure() + tw = _pytest.config.create_terminal_writer(config) + for line in config.getini("markers"): + name, rest = line.split(":", 1) + tw.write("@pytest.mark.%s:" % name, bold=True) + tw.line(rest) + tw.line() + config._ensure_unconfigure() + return 0 + + +pytest_cmdline_main.tryfirst = True + + +def pytest_collection_modifyitems(items, config): + keywordexpr = config.option.keyword.lstrip() + matchexpr = config.option.markexpr + if not keywordexpr and not matchexpr: + return + # pytest used to allow "-" for negating + # but today we just allow "-" at the beginning, use "not" instead + # we probably remove "-" altogether soon + if keywordexpr.startswith("-"): + keywordexpr = "not " + keywordexpr[1:] + selectuntil = False + if keywordexpr[-1:] == ":": + selectuntil = True + keywordexpr = keywordexpr[:-1] + + remaining = [] + deselected = [] + for colitem in items: + if keywordexpr and not matchkeyword(colitem, keywordexpr): + deselected.append(colitem) + else: + if selectuntil: + keywordexpr = None + if matchexpr: + if not matchmark(colitem, matchexpr): + deselected.append(colitem) + continue + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +class MarkMapping: + """Provides a local mapping for markers where item access + resolves to True if the marker is present. """ + def __init__(self, keywords): + mymarks = set() + for key, value in keywords.items(): + if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator): + mymarks.add(key) + self._mymarks = mymarks + + def __getitem__(self, name): + return name in self._mymarks + + +class KeywordMapping: + """Provides a local mapping for keywords. + Given a list of names, map any substring of one of these names to True. + """ + def __init__(self, names): + self._names = names + + def __getitem__(self, subname): + for name in self._names: + if subname in name: + return True + return False + + +def matchmark(colitem, markexpr): + """Tries to match on any marker names, attached to the given colitem.""" + return eval(markexpr, {}, MarkMapping(colitem.keywords)) + + +def matchkeyword(colitem, keywordexpr): + """Tries to match given keyword expression to given collector item. + + Will match on the name of colitem, including the names of its parents. + Only matches names of items which are either a :class:`Class` or a + :class:`Function`. + Additionally, matches on names in the 'extra_keyword_matches' set of + any item, as well as names directly assigned to test functions. + """ + mapped_names = set() + + # Add the names of the current item and any parent items + import pytest + for item in colitem.listchain(): + if not isinstance(item, pytest.Instance): + mapped_names.add(item.name) + + # Add the names added as extra keywords to current or parent items + for name in colitem.listextrakeywords(): + mapped_names.add(name) + + # Add the names attached to the current function through direct assignment + if hasattr(colitem, 'function'): + for name in colitem.function.__dict__: + mapped_names.add(name) + + mapping = KeywordMapping(mapped_names) + if " " not in keywordexpr: + # special case to allow for simple "-k pass" and "-k 1.3" + return mapping[keywordexpr] + elif keywordexpr.startswith("not ") and " " not in keywordexpr[4:]: + return not mapping[keywordexpr[4:]] + return eval(keywordexpr, {}, mapping) + + +def pytest_configure(config): + config._old_mark_config = MARK_GEN._config + if config.option.strict: + MARK_GEN._config = config + + +def pytest_unconfigure(config): + MARK_GEN._config = getattr(config, '_old_mark_config', None) + + +class MarkGenerator: + """ Factory for :class:`MarkDecorator` objects - exposed as + a ``pytest.mark`` singleton instance. Example:: + + import pytest + @pytest.mark.slowtest + def test_function(): + pass + + will set a 'slowtest' :class:`MarkInfo` object + on the ``test_function`` object. """ + _config = None + + + def __getattr__(self, name): + if name[0] == "_": + raise AttributeError("Marker name must NOT start with underscore") + if self._config is not None: + self._check(name) + return MarkDecorator(Mark(name, (), {})) + + def _check(self, name): + try: + if name in self._markers: + return + except AttributeError: + pass + self._markers = l = set() + for line in self._config.getini("markers"): + beginning = line.split(":", 1) + x = beginning[0].split("(", 1)[0] + l.add(x) + if name not in self._markers: + raise AttributeError("%r not a registered marker" % (name,)) + + +def istestfunc(func): + return hasattr(func, "__call__") and \ + getattr(func, "__name__", "") != "" + +class MarkDecorator: + """ A decorator for test functions and test classes. When applied + it will create :class:`MarkInfo` objects which may be + :ref:`retrieved by hooks as item keywords `. + MarkDecorator instances are often created like this:: + + mark1 = pytest.mark.NAME # simple MarkDecorator + mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator + + and can then be applied as decorators to test functions:: + + @mark2 + def test_function(): + pass + + When a MarkDecorator instance is called it does the following: + 1. If called with a single class as its only positional argument and no + additional keyword arguments, it attaches itself to the class so it + gets applied automatically to all test cases found in that class. + 2. If called with a single function as its only positional argument and + no additional keyword arguments, it attaches a MarkInfo object to the + function, containing all the arguments already stored internally in + the MarkDecorator. + 3. When called in any other case, it performs a 'fake construction' call, + i.e. it returns a new MarkDecorator instance with the original + MarkDecorator's content updated with the arguments passed to this + call. + + Note: The rules above prevent MarkDecorator objects from storing only a + single function or class reference as their positional argument with no + additional keyword or positional arguments. + + """ + def __init__(self, mark): + assert isinstance(mark, Mark), repr(mark) + self.mark = mark + + name = alias('mark.name') + args = alias('mark.args') + kwargs = alias('mark.kwargs') + + @property + def markname(self): + return self.name # for backward-compat (2.4.1 had this attr) + + def __eq__(self, other): + return self.mark == other.mark + + def __repr__(self): + return "" % (self.mark,) + + def __call__(self, *args, **kwargs): + """ if passed a single callable argument: decorate it with mark info. + otherwise add *args/**kwargs in-place to mark information. """ + if args and not kwargs: + func = args[0] + is_class = inspect.isclass(func) + if len(args) == 1 and (istestfunc(func) or is_class): + if is_class: + if hasattr(func, 'pytestmark'): + mark_list = func.pytestmark + if not isinstance(mark_list, list): + mark_list = [mark_list] + # always work on a copy to avoid updating pytestmark + # from a superclass by accident + mark_list = mark_list + [self] + func.pytestmark = mark_list + else: + func.pytestmark = [self] + else: + holder = getattr(func, self.name, None) + if holder is None: + holder = MarkInfo(self.mark) + setattr(func, self.name, holder) + else: + holder.add_mark(self.mark) + return func + + mark = Mark(self.name, args, kwargs) + return self.__class__(self.mark.combined_with(mark)) + + + + + +class Mark(namedtuple('Mark', 'name, args, kwargs')): + + def combined_with(self, other): + assert self.name == other.name + return Mark( + self.name, self.args + other.args, + dict(self.kwargs, **other.kwargs)) + + +class MarkInfo(object): + """ Marking object created by :class:`MarkDecorator` instances. """ + def __init__(self, mark): + assert isinstance(mark, Mark), repr(mark) + self.combined = mark + self._marks = [mark] + + name = alias('combined.name') + args = alias('combined.args') + kwargs = alias('combined.kwargs') + + def __repr__(self): + return "".format(self.combined) + + def add_mark(self, mark): + """ add a MarkInfo with the given args and kwargs. """ + self._marks.append(mark) + self.combined = self.combined.combined_with(mark) + + def __iter__(self): + """ yield MarkInfo objects each relating to a marking-call. """ + return imap(MarkInfo, self._marks) + + +MARK_GEN = MarkGenerator() diff --git a/venv/lib/python3.5/site-packages/_pytest/monkeypatch.py b/venv/lib/python3.5/site-packages/_pytest/monkeypatch.py new file mode 100644 index 0000000..a70b23d --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/monkeypatch.py @@ -0,0 +1,259 @@ +""" monkeypatching and mocking functionality. """ +from __future__ import absolute_import, division, print_function + +import os +import sys +import re + +from py.builtin import _basestring +from _pytest.fixtures import fixture + +RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$") + + +@fixture +def monkeypatch(): + """The returned ``monkeypatch`` fixture provides these + helper methods to modify objects, dictionaries or os.environ:: + + monkeypatch.setattr(obj, name, value, raising=True) + monkeypatch.delattr(obj, name, raising=True) + monkeypatch.setitem(mapping, name, value) + monkeypatch.delitem(obj, name, raising=True) + monkeypatch.setenv(name, value, prepend=False) + monkeypatch.delenv(name, value, raising=True) + monkeypatch.syspath_prepend(path) + monkeypatch.chdir(path) + + All modifications will be undone after the requesting + test function or fixture has finished. The ``raising`` + parameter determines if a KeyError or AttributeError + will be raised if the set/deletion operation has no target. + """ + mpatch = MonkeyPatch() + yield mpatch + mpatch.undo() + + +def resolve(name): + # simplified from zope.dottedname + parts = name.split('.') + + used = parts.pop(0) + found = __import__(used) + for part in parts: + used += '.' + part + try: + found = getattr(found, part) + except AttributeError: + pass + else: + continue + # we use explicit un-nesting of the handling block in order + # to avoid nested exceptions on python 3 + try: + __import__(used) + except ImportError as ex: + # str is used for py2 vs py3 + expected = str(ex).split()[-1] + if expected == used: + raise + else: + raise ImportError( + 'import error in %s: %s' % (used, ex) + ) + found = annotated_getattr(found, part, used) + return found + + +def annotated_getattr(obj, name, ann): + try: + obj = getattr(obj, name) + except AttributeError: + raise AttributeError( + '%r object at %s has no attribute %r' % ( + type(obj).__name__, ann, name + ) + ) + return obj + + +def derive_importpath(import_path, raising): + if not isinstance(import_path, _basestring) or "." not in import_path: + raise TypeError("must be absolute import path string, not %r" % + (import_path,)) + module, attr = import_path.rsplit('.', 1) + target = resolve(module) + if raising: + annotated_getattr(target, attr, ann=module) + return attr, target + + +class Notset: + def __repr__(self): + return "" + + +notset = Notset() + + +class MonkeyPatch: + """ Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes. + """ + + def __init__(self): + self._setattr = [] + self._setitem = [] + self._cwd = None + self._savesyspath = None + + def setattr(self, target, name, value=notset, raising=True): + """ Set attribute value on target, memorizing the old value. + By default raise AttributeError if the attribute did not exist. + + For convenience you can specify a string as ``target`` which + will be interpreted as a dotted import path, with the last part + being the attribute name. Example: + ``monkeypatch.setattr("os.getcwd", lambda x: "/")`` + would set the ``getcwd`` function of the ``os`` module. + + The ``raising`` value determines if the setattr should fail + if the attribute is not already present (defaults to True + which means it will raise). + """ + __tracebackhide__ = True + import inspect + + if value is notset: + if not isinstance(target, _basestring): + raise TypeError("use setattr(target, name, value) or " + "setattr(target, value) with target being a dotted " + "import string") + value = name + name, target = derive_importpath(target, raising) + + oldval = getattr(target, name, notset) + if raising and oldval is notset: + raise AttributeError("%r has no attribute %r" % (target, name)) + + # avoid class descriptors like staticmethod/classmethod + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + setattr(target, name, value) + + def delattr(self, target, name=notset, raising=True): + """ Delete attribute ``name`` from ``target``, by default raise + AttributeError it the attribute did not previously exist. + + If no ``name`` is specified and ``target`` is a string + it will be interpreted as a dotted import path with the + last part being the attribute name. + + If ``raising`` is set to False, no exception will be raised if the + attribute is missing. + """ + __tracebackhide__ = True + if name is notset: + if not isinstance(target, _basestring): + raise TypeError("use delattr(target, name) or " + "delattr(target) with target being a dotted " + "import string") + name, target = derive_importpath(target, raising) + + if not hasattr(target, name): + if raising: + raise AttributeError(name) + else: + self._setattr.append((target, name, getattr(target, name, notset))) + delattr(target, name) + + def setitem(self, dic, name, value): + """ Set dictionary entry ``name`` to value. """ + self._setitem.append((dic, name, dic.get(name, notset))) + dic[name] = value + + def delitem(self, dic, name, raising=True): + """ Delete ``name`` from dict. Raise KeyError if it doesn't exist. + + If ``raising`` is set to False, no exception will be raised if the + key is missing. + """ + if name not in dic: + if raising: + raise KeyError(name) + else: + self._setitem.append((dic, name, dic.get(name, notset))) + del dic[name] + + def setenv(self, name, value, prepend=None): + """ Set environment variable ``name`` to ``value``. If ``prepend`` + is a character, read the current environment variable value + and prepend the ``value`` adjoined with the ``prepend`` character.""" + value = str(value) + if prepend and name in os.environ: + value = value + prepend + os.environ[name] + self.setitem(os.environ, name, value) + + def delenv(self, name, raising=True): + """ Delete ``name`` from the environment. Raise KeyError it does not + exist. + + If ``raising`` is set to False, no exception will be raised if the + environment variable is missing. + """ + self.delitem(os.environ, name, raising=raising) + + def syspath_prepend(self, path): + """ Prepend ``path`` to ``sys.path`` list of import locations. """ + if self._savesyspath is None: + self._savesyspath = sys.path[:] + sys.path.insert(0, str(path)) + + def chdir(self, path): + """ Change the current working directory to the specified path. + Path can be a string or a py.path.local object. + """ + if self._cwd is None: + self._cwd = os.getcwd() + if hasattr(path, "chdir"): + path.chdir() + else: + os.chdir(path) + + def undo(self): + """ Undo previous changes. This call consumes the + undo stack. Calling it a second time has no effect unless + you do more monkeypatching after the undo call. + + There is generally no need to call `undo()`, since it is + called automatically during tear-down. + + Note that the same `monkeypatch` fixture is used across a + single test function invocation. If `monkeypatch` is used both by + the test function itself and one of the test fixtures, + calling `undo()` will undo all of the changes made in + both functions. + """ + for obj, name, value in reversed(self._setattr): + if value is not notset: + setattr(obj, name, value) + else: + delattr(obj, name) + self._setattr[:] = [] + for dictionary, name, value in reversed(self._setitem): + if value is notset: + try: + del dictionary[name] + except KeyError: + pass # was already deleted, so we have the desired state + else: + dictionary[name] = value + self._setitem[:] = [] + if self._savesyspath is not None: + sys.path[:] = self._savesyspath + self._savesyspath = None + + if self._cwd is not None: + os.chdir(self._cwd) + self._cwd = None diff --git a/venv/lib/python3.5/site-packages/_pytest/nose.py b/venv/lib/python3.5/site-packages/_pytest/nose.py new file mode 100644 index 0000000..9d4fc0b --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/nose.py @@ -0,0 +1,72 @@ +""" run test suites written for nose. """ +from __future__ import absolute_import, division, print_function + +import sys + +import py +from _pytest import unittest, runner, python +from _pytest.config import hookimpl + + +def get_skip_exceptions(): + skip_classes = set() + for module_name in ('unittest', 'unittest2', 'nose'): + mod = sys.modules.get(module_name) + if hasattr(mod, 'SkipTest'): + skip_classes.add(mod.SkipTest) + return tuple(skip_classes) + + +def pytest_runtest_makereport(item, call): + if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()): + # let's substitute the excinfo with a pytest.skip one + call2 = call.__class__( + lambda: runner.skip(str(call.excinfo.value)), call.when) + call.excinfo = call2.excinfo + + +@hookimpl(trylast=True) +def pytest_runtest_setup(item): + if is_potential_nosetest(item): + if isinstance(item.parent, python.Generator): + gen = item.parent + if not hasattr(gen, '_nosegensetup'): + call_optional(gen.obj, 'setup') + if isinstance(gen.parent, python.Instance): + call_optional(gen.parent.obj, 'setup') + gen._nosegensetup = True + if not call_optional(item.obj, 'setup'): + # call module level setup if there is no object level one + call_optional(item.parent.obj, 'setup') + #XXX this implies we only call teardown when setup worked + item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item) + +def teardown_nose(item): + if is_potential_nosetest(item): + if not call_optional(item.obj, 'teardown'): + call_optional(item.parent.obj, 'teardown') + #if hasattr(item.parent, '_nosegensetup'): + # #call_optional(item._nosegensetup, 'teardown') + # del item.parent._nosegensetup + + +def pytest_make_collect_report(collector): + if isinstance(collector, python.Generator): + call_optional(collector.obj, 'setup') + + +def is_potential_nosetest(item): + # extra check needed since we do not do nose style setup/teardown + # on direct unittest style classes + return isinstance(item, python.Function) and \ + not isinstance(item, unittest.TestCaseFunction) + + +def call_optional(obj, name): + method = getattr(obj, name, None) + isfixture = hasattr(method, "_pytestfixturefunction") + if method is not None and not isfixture and py.builtin.callable(method): + # If there's any problems allow the exception to raise rather than + # silently ignoring them + method() + return True diff --git a/venv/lib/python3.5/site-packages/_pytest/pastebin.py b/venv/lib/python3.5/site-packages/_pytest/pastebin.py new file mode 100644 index 0000000..6f3ce8f --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/pastebin.py @@ -0,0 +1,100 @@ +""" submit failure or test session information to a pastebin service. """ +from __future__ import absolute_import, division, print_function + +import pytest +import sys +import tempfile + + +def pytest_addoption(parser): + group = parser.getgroup("terminal reporting") + group._addoption('--pastebin', metavar="mode", + action='store', dest="pastebin", default=None, + choices=['failed', 'all'], + help="send failed|all info to bpaste.net pastebin service.") + + +@pytest.hookimpl(trylast=True) +def pytest_configure(config): + import py + if config.option.pastebin == "all": + tr = config.pluginmanager.getplugin('terminalreporter') + # if no terminal reporter plugin is present, nothing we can do here; + # this can happen when this function executes in a slave node + # when using pytest-xdist, for example + if tr is not None: + # pastebin file will be utf-8 encoded binary file + config._pastebinfile = tempfile.TemporaryFile('w+b') + oldwrite = tr._tw.write + + def tee_write(s, **kwargs): + oldwrite(s, **kwargs) + if py.builtin._istext(s): + s = s.encode('utf-8') + config._pastebinfile.write(s) + + tr._tw.write = tee_write + + +def pytest_unconfigure(config): + if hasattr(config, '_pastebinfile'): + # get terminal contents and delete file + config._pastebinfile.seek(0) + sessionlog = config._pastebinfile.read() + config._pastebinfile.close() + del config._pastebinfile + # undo our patching in the terminal reporter + tr = config.pluginmanager.getplugin('terminalreporter') + del tr._tw.__dict__['write'] + # write summary + tr.write_sep("=", "Sending information to Paste Service") + pastebinurl = create_new_paste(sessionlog) + tr.write_line("pastebin session-log: %s\n" % pastebinurl) + + +def create_new_paste(contents): + """ + Creates a new paste using bpaste.net service. + + :contents: paste contents as utf-8 encoded bytes + :returns: url to the pasted contents + """ + import re + if sys.version_info < (3, 0): + from urllib import urlopen, urlencode + else: + from urllib.request import urlopen + from urllib.parse import urlencode + + params = { + 'code': contents, + 'lexer': 'python3' if sys.version_info[0] == 3 else 'python', + 'expiry': '1week', + } + url = 'https://bpaste.net' + response = urlopen(url, data=urlencode(params).encode('ascii')).read() + m = re.search(r'href="/raw/(\w+)"', response.decode('utf-8')) + if m: + return '%s/show/%s' % (url, m.group(1)) + else: + return 'bad response: ' + response + + +def pytest_terminal_summary(terminalreporter): + import _pytest.config + if terminalreporter.config.option.pastebin != "failed": + return + tr = terminalreporter + if 'failed' in tr.stats: + terminalreporter.write_sep("=", "Sending information to Paste Service") + for rep in terminalreporter.stats.get('failed'): + try: + msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc + except AttributeError: + msg = tr._getfailureheadline(rep) + tw = _pytest.config.create_terminal_writer(terminalreporter.config, stringio=True) + rep.toterminal(tw) + s = tw.stringio.getvalue() + assert len(s) + pastebinurl = create_new_paste(s) + tr.write_line("%s --> %s" %(msg, pastebinurl)) diff --git a/venv/lib/python3.5/site-packages/_pytest/pytester.py b/venv/lib/python3.5/site-packages/_pytest/pytester.py new file mode 100644 index 0000000..901caa3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/pytester.py @@ -0,0 +1,1151 @@ +""" (disabled by default) support for testing pytest and pytest plugins. """ +from __future__ import absolute_import, division, print_function + +import codecs +import gc +import os +import platform +import re +import subprocess +import sys +import time +import traceback +from fnmatch import fnmatch + +from weakref import WeakKeyDictionary + +from _pytest.capture import MultiCapture, SysCapture +from _pytest._code import Source +import py +import pytest +from _pytest.main import Session, EXIT_OK +from _pytest.assertion.rewrite import AssertionRewritingHook + + +def pytest_addoption(parser): + # group = parser.getgroup("pytester", "pytester (self-tests) options") + parser.addoption('--lsof', + action="store_true", dest="lsof", default=False, + help=("run FD checks if lsof is available")) + + parser.addoption('--runpytest', default="inprocess", dest="runpytest", + choices=("inprocess", "subprocess", ), + help=("run pytest sub runs in tests using an 'inprocess' " + "or 'subprocess' (python -m main) method")) + + +def pytest_configure(config): + # This might be called multiple times. Only take the first. + global _pytest_fullpath + try: + _pytest_fullpath + except NameError: + _pytest_fullpath = os.path.abspath(pytest.__file__.rstrip("oc")) + _pytest_fullpath = _pytest_fullpath.replace("$py.class", ".py") + + if config.getvalue("lsof"): + checker = LsofFdLeakChecker() + if checker.matching_platform(): + config.pluginmanager.register(checker) + + +class LsofFdLeakChecker(object): + def get_open_files(self): + out = self._exec_lsof() + open_files = self._parse_lsof_output(out) + return open_files + + def _exec_lsof(self): + pid = os.getpid() + return py.process.cmdexec("lsof -Ffn0 -p %d" % pid) + + def _parse_lsof_output(self, out): + def isopen(line): + return line.startswith('f') and ("deleted" not in line and + 'mem' not in line and "txt" not in line and 'cwd' not in line) + + open_files = [] + + for line in out.split("\n"): + if isopen(line): + fields = line.split('\0') + fd = fields[0][1:] + filename = fields[1][1:] + if filename.startswith('/'): + open_files.append((fd, filename)) + + return open_files + + def matching_platform(self): + try: + py.process.cmdexec("lsof -v") + except (py.process.cmdexec.Error, UnicodeDecodeError): + # cmdexec may raise UnicodeDecodeError on Windows systems + # with locale other than english: + # https://bitbucket.org/pytest-dev/py/issues/66 + return False + else: + return True + + @pytest.hookimpl(hookwrapper=True, tryfirst=True) + def pytest_runtest_protocol(self, item): + lines1 = self.get_open_files() + yield + if hasattr(sys, "pypy_version_info"): + gc.collect() + lines2 = self.get_open_files() + + new_fds = set([t[0] for t in lines2]) - set([t[0] for t in lines1]) + leaked_files = [t for t in lines2 if t[0] in new_fds] + if leaked_files: + error = [] + error.append("***** %s FD leakage detected" % len(leaked_files)) + error.extend([str(f) for f in leaked_files]) + error.append("*** Before:") + error.extend([str(f) for f in lines1]) + error.append("*** After:") + error.extend([str(f) for f in lines2]) + error.append(error[0]) + error.append("*** function %s:%s: %s " % item.location) + error.append("See issue #2366") + item.warn('', "\n".join(error)) + + +# XXX copied from execnet's conftest.py - needs to be merged +winpymap = { + 'python2.7': r'C:\Python27\python.exe', + 'python2.6': r'C:\Python26\python.exe', + 'python3.1': r'C:\Python31\python.exe', + 'python3.2': r'C:\Python32\python.exe', + 'python3.3': r'C:\Python33\python.exe', + 'python3.4': r'C:\Python34\python.exe', + 'python3.5': r'C:\Python35\python.exe', +} + +def getexecutable(name, cache={}): + try: + return cache[name] + except KeyError: + executable = py.path.local.sysfind(name) + if executable: + import subprocess + popen = subprocess.Popen([str(executable), "--version"], + universal_newlines=True, stderr=subprocess.PIPE) + out, err = popen.communicate() + if name == "jython": + if not err or "2.5" not in err: + executable = None + if "2.5.2" in err: + executable = None # http://bugs.jython.org/issue1790 + elif popen.returncode != 0: + # Handle pyenv's 127. + executable = None + cache[name] = executable + return executable + +@pytest.fixture(params=['python2.6', 'python2.7', 'python3.3', "python3.4", + 'pypy', 'pypy3']) +def anypython(request): + name = request.param + executable = getexecutable(name) + if executable is None: + if sys.platform == "win32": + executable = winpymap.get(name, None) + if executable: + executable = py.path.local(executable) + if executable.check(): + return executable + pytest.skip("no suitable %s found" % (name,)) + return executable + +# used at least by pytest-xdist plugin +@pytest.fixture +def _pytest(request): + """ Return a helper which offers a gethookrecorder(hook) + method which returns a HookRecorder instance which helps + to make assertions about called hooks. + """ + return PytestArg(request) + +class PytestArg: + def __init__(self, request): + self.request = request + + def gethookrecorder(self, hook): + hookrecorder = HookRecorder(hook._pm) + self.request.addfinalizer(hookrecorder.finish_recording) + return hookrecorder + + +def get_public_names(l): + """Only return names from iterator l without a leading underscore.""" + return [x for x in l if x[0] != "_"] + + +class ParsedCall: + def __init__(self, name, kwargs): + self.__dict__.update(kwargs) + self._name = name + + def __repr__(self): + d = self.__dict__.copy() + del d['_name'] + return "" %(self._name, d) + + +class HookRecorder: + """Record all hooks called in a plugin manager. + + This wraps all the hook calls in the plugin manager, recording + each call before propagating the normal calls. + + """ + + def __init__(self, pluginmanager): + self._pluginmanager = pluginmanager + self.calls = [] + + def before(hook_name, hook_impls, kwargs): + self.calls.append(ParsedCall(hook_name, kwargs)) + + def after(outcome, hook_name, hook_impls, kwargs): + pass + + self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after) + + def finish_recording(self): + self._undo_wrapping() + + def getcalls(self, names): + if isinstance(names, str): + names = names.split() + return [call for call in self.calls if call._name in names] + + def assert_contains(self, entries): + __tracebackhide__ = True + i = 0 + entries = list(entries) + backlocals = sys._getframe(1).f_locals + while entries: + name, check = entries.pop(0) + for ind, call in enumerate(self.calls[i:]): + if call._name == name: + print("NAMEMATCH", name, call) + if eval(check, backlocals, call.__dict__): + print("CHECKERMATCH", repr(check), "->", call) + else: + print("NOCHECKERMATCH", repr(check), "-", call) + continue + i += ind + 1 + break + print("NONAMEMATCH", name, "with", call) + else: + pytest.fail("could not find %r check %r" % (name, check)) + + def popcall(self, name): + __tracebackhide__ = True + for i, call in enumerate(self.calls): + if call._name == name: + del self.calls[i] + return call + lines = ["could not find call %r, in:" % (name,)] + lines.extend([" %s" % str(x) for x in self.calls]) + pytest.fail("\n".join(lines)) + + def getcall(self, name): + l = self.getcalls(name) + assert len(l) == 1, (name, l) + return l[0] + + # functionality for test reports + + def getreports(self, + names="pytest_runtest_logreport pytest_collectreport"): + return [x.report for x in self.getcalls(names)] + + def matchreport(self, inamepart="", + names="pytest_runtest_logreport pytest_collectreport", when=None): + """ return a testreport whose dotted import path matches """ + l = [] + for rep in self.getreports(names=names): + try: + if not when and rep.when != "call" and rep.passed: + # setup/teardown passing reports - let's ignore those + continue + except AttributeError: + pass + if when and getattr(rep, 'when', None) != when: + continue + if not inamepart or inamepart in rep.nodeid.split("::"): + l.append(rep) + if not l: + raise ValueError("could not find test report matching %r: " + "no test reports at all!" % (inamepart,)) + if len(l) > 1: + raise ValueError( + "found 2 or more testreports matching %r: %s" %(inamepart, l)) + return l[0] + + def getfailures(self, + names='pytest_runtest_logreport pytest_collectreport'): + return [rep for rep in self.getreports(names) if rep.failed] + + def getfailedcollections(self): + return self.getfailures('pytest_collectreport') + + def listoutcomes(self): + passed = [] + skipped = [] + failed = [] + for rep in self.getreports( + "pytest_collectreport pytest_runtest_logreport"): + if rep.passed: + if getattr(rep, "when", None) == "call": + passed.append(rep) + elif rep.skipped: + skipped.append(rep) + elif rep.failed: + failed.append(rep) + return passed, skipped, failed + + def countoutcomes(self): + return [len(x) for x in self.listoutcomes()] + + def assertoutcome(self, passed=0, skipped=0, failed=0): + realpassed, realskipped, realfailed = self.listoutcomes() + assert passed == len(realpassed) + assert skipped == len(realskipped) + assert failed == len(realfailed) + + def clear(self): + self.calls[:] = [] + + +@pytest.fixture +def linecomp(request): + return LineComp() + + +@pytest.fixture(name='LineMatcher') +def LineMatcher_fixture(request): + return LineMatcher + + +@pytest.fixture +def testdir(request, tmpdir_factory): + return Testdir(request, tmpdir_factory) + + +rex_outcome = re.compile(r"(\d+) ([\w-]+)") +class RunResult: + """The result of running a command. + + Attributes: + + :ret: The return value. + :outlines: List of lines captured from stdout. + :errlines: List of lines captures from stderr. + :stdout: :py:class:`LineMatcher` of stdout, use ``stdout.str()`` to + reconstruct stdout or the commonly used + ``stdout.fnmatch_lines()`` method. + :stderrr: :py:class:`LineMatcher` of stderr. + :duration: Duration in seconds. + + """ + def __init__(self, ret, outlines, errlines, duration): + self.ret = ret + self.outlines = outlines + self.errlines = errlines + self.stdout = LineMatcher(outlines) + self.stderr = LineMatcher(errlines) + self.duration = duration + + def parseoutcomes(self): + """ Return a dictionary of outcomestring->num from parsing + the terminal output that the test process produced.""" + for line in reversed(self.outlines): + if 'seconds' in line: + outcomes = rex_outcome.findall(line) + if outcomes: + d = {} + for num, cat in outcomes: + d[cat] = int(num) + return d + raise ValueError("Pytest terminal report not found") + + def assert_outcomes(self, passed=0, skipped=0, failed=0): + """ assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" + d = self.parseoutcomes() + assert passed == d.get("passed", 0) + assert skipped == d.get("skipped", 0) + assert failed == d.get("failed", 0) + + + +class Testdir: + """Temporary test directory with tools to test/run pytest itself. + + This is based on the ``tmpdir`` fixture but provides a number of + methods which aid with testing pytest itself. Unless + :py:meth:`chdir` is used all methods will use :py:attr:`tmpdir` as + current working directory. + + Attributes: + + :tmpdir: The :py:class:`py.path.local` instance of the temporary + directory. + + :plugins: A list of plugins to use with :py:meth:`parseconfig` and + :py:meth:`runpytest`. Initially this is an empty list but + plugins can be added to the list. The type of items to add to + the list depend on the method which uses them so refer to them + for details. + + """ + + def __init__(self, request, tmpdir_factory): + self.request = request + self._mod_collections = WeakKeyDictionary() + # XXX remove duplication with tmpdir plugin + basetmp = tmpdir_factory.ensuretemp("testdir") + name = request.function.__name__ + for i in range(100): + try: + tmpdir = basetmp.mkdir(name + str(i)) + except py.error.EEXIST: + continue + break + self.tmpdir = tmpdir + self.plugins = [] + self._savesyspath = (list(sys.path), list(sys.meta_path)) + self._savemodulekeys = set(sys.modules) + self.chdir() # always chdir + self.request.addfinalizer(self.finalize) + method = self.request.config.getoption("--runpytest") + if method == "inprocess": + self._runpytest_method = self.runpytest_inprocess + elif method == "subprocess": + self._runpytest_method = self.runpytest_subprocess + + def __repr__(self): + return "" % (self.tmpdir,) + + def finalize(self): + """Clean up global state artifacts. + + Some methods modify the global interpreter state and this + tries to clean this up. It does not remove the temporary + directory however so it can be looked at after the test run + has finished. + + """ + sys.path[:], sys.meta_path[:] = self._savesyspath + if hasattr(self, '_olddir'): + self._olddir.chdir() + self.delete_loaded_modules() + + def delete_loaded_modules(self): + """Delete modules that have been loaded during a test. + + This allows the interpreter to catch module changes in case + the module is re-imported. + """ + for name in set(sys.modules).difference(self._savemodulekeys): + # some zope modules used by twisted-related tests keeps internal + # state and can't be deleted; we had some trouble in the past + # with zope.interface for example + if not name.startswith("zope"): + del sys.modules[name] + + def make_hook_recorder(self, pluginmanager): + """Create a new :py:class:`HookRecorder` for a PluginManager.""" + assert not hasattr(pluginmanager, "reprec") + pluginmanager.reprec = reprec = HookRecorder(pluginmanager) + self.request.addfinalizer(reprec.finish_recording) + return reprec + + def chdir(self): + """Cd into the temporary directory. + + This is done automatically upon instantiation. + + """ + old = self.tmpdir.chdir() + if not hasattr(self, '_olddir'): + self._olddir = old + + def _makefile(self, ext, args, kwargs, encoding="utf-8"): + items = list(kwargs.items()) + if args: + source = py.builtin._totext("\n").join( + map(py.builtin._totext, args)) + py.builtin._totext("\n") + basename = self.request.function.__name__ + items.insert(0, (basename, source)) + ret = None + for name, value in items: + p = self.tmpdir.join(name).new(ext=ext) + p.dirpath().ensure_dir() + source = Source(value) + + def my_totext(s, encoding="utf-8"): + if py.builtin._isbytes(s): + s = py.builtin._totext(s, encoding=encoding) + return s + + source_unicode = "\n".join([my_totext(line) for line in source.lines]) + source = py.builtin._totext(source_unicode) + content = source.strip().encode(encoding) # + "\n" + #content = content.rstrip() + "\n" + p.write(content, "wb") + if ret is None: + ret = p + return ret + + def makefile(self, ext, *args, **kwargs): + """Create a new file in the testdir. + + ext: The extension the file should use, including the dot. + E.g. ".py". + + args: All args will be treated as strings and joined using + newlines. The result will be written as contents to the + file. The name of the file will be based on the test + function requesting this fixture. + E.g. "testdir.makefile('.txt', 'line1', 'line2')" + + kwargs: Each keyword is the name of a file, while the value of + it will be written as contents of the file. + E.g. "testdir.makefile('.ini', pytest='[pytest]\naddopts=-rs\n')" + + """ + return self._makefile(ext, args, kwargs) + + def makeconftest(self, source): + """Write a contest.py file with 'source' as contents.""" + return self.makepyfile(conftest=source) + + def makeini(self, source): + """Write a tox.ini file with 'source' as contents.""" + return self.makefile('.ini', tox=source) + + def getinicfg(self, source): + """Return the pytest section from the tox.ini config file.""" + p = self.makeini(source) + return py.iniconfig.IniConfig(p)['pytest'] + + def makepyfile(self, *args, **kwargs): + """Shortcut for .makefile() with a .py extension.""" + return self._makefile('.py', args, kwargs) + + def maketxtfile(self, *args, **kwargs): + """Shortcut for .makefile() with a .txt extension.""" + return self._makefile('.txt', args, kwargs) + + def syspathinsert(self, path=None): + """Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`. + + This is undone automatically after the test. + """ + if path is None: + path = self.tmpdir + sys.path.insert(0, str(path)) + # a call to syspathinsert() usually means that the caller + # wants to import some dynamically created files. + # with python3 we thus invalidate import caches. + self._possibly_invalidate_import_caches() + + def _possibly_invalidate_import_caches(self): + # invalidate caches if we can (py33 and above) + try: + import importlib + except ImportError: + pass + else: + if hasattr(importlib, "invalidate_caches"): + importlib.invalidate_caches() + + def mkdir(self, name): + """Create a new (sub)directory.""" + return self.tmpdir.mkdir(name) + + def mkpydir(self, name): + """Create a new python package. + + This creates a (sub)directory with an empty ``__init__.py`` + file so that is recognised as a python package. + + """ + p = self.mkdir(name) + p.ensure("__init__.py") + return p + + Session = Session + def getnode(self, config, arg): + """Return the collection node of a file. + + :param config: :py:class:`_pytest.config.Config` instance, see + :py:meth:`parseconfig` and :py:meth:`parseconfigure` to + create the configuration. + + :param arg: A :py:class:`py.path.local` instance of the file. + + """ + session = Session(config) + assert '::' not in str(arg) + p = py.path.local(arg) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([str(p)], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK) + return res + + def getpathnode(self, path): + """Return the collection node of a file. + + This is like :py:meth:`getnode` but uses + :py:meth:`parseconfigure` to create the (configured) pytest + Config instance. + + :param path: A :py:class:`py.path.local` instance of the file. + + """ + config = self.parseconfigure(path) + session = Session(config) + x = session.fspath.bestrelpath(path) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([x], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK) + return res + + def genitems(self, colitems): + """Generate all test items from a collection node. + + This recurses into the collection node and returns a list of + all the test items contained within. + + """ + session = colitems[0].session + result = [] + for colitem in colitems: + result.extend(session.genitems(colitem)) + return result + + def runitem(self, source): + """Run the "test_func" Item. + + The calling test instance (the class which contains the test + method) must provide a ``.getrunner()`` method which should + return a runner which can run the test protocol for a single + item, like e.g. :py:func:`_pytest.runner.runtestprotocol`. + + """ + # used from runner functional tests + item = self.getitem(source) + # the test class where we are called from wants to provide the runner + testclassinstance = self.request.instance + runner = testclassinstance.getrunner() + return runner(item) + + def inline_runsource(self, source, *cmdlineargs): + """Run a test module in process using ``pytest.main()``. + + This run writes "source" into a temporary file and runs + ``pytest.main()`` on it, returning a :py:class:`HookRecorder` + instance for the result. + + :param source: The source code of the test module. + + :param cmdlineargs: Any extra command line arguments to use. + + :return: :py:class:`HookRecorder` instance of the result. + + """ + p = self.makepyfile(source) + l = list(cmdlineargs) + [p] + return self.inline_run(*l) + + def inline_genitems(self, *args): + """Run ``pytest.main(['--collectonly'])`` in-process. + + Returns a tuple of the collected items and a + :py:class:`HookRecorder` instance. + + This runs the :py:func:`pytest.main` function to run all of + pytest inside the test process itself like + :py:meth:`inline_run`. However the return value is a tuple of + the collection items and a :py:class:`HookRecorder` instance. + + """ + rec = self.inline_run("--collect-only", *args) + items = [x.item for x in rec.getcalls("pytest_itemcollected")] + return items, rec + + def inline_run(self, *args, **kwargs): + """Run ``pytest.main()`` in-process, returning a HookRecorder. + + This runs the :py:func:`pytest.main` function to run all of + pytest inside the test process itself. This means it can + return a :py:class:`HookRecorder` instance which gives more + detailed results from then run then can be done by matching + stdout/stderr from :py:meth:`runpytest`. + + :param args: Any command line arguments to pass to + :py:func:`pytest.main`. + + :param plugin: (keyword-only) Extra plugin instances the + ``pytest.main()`` instance should use. + + :return: A :py:class:`HookRecorder` instance. + """ + # When running py.test inline any plugins active in the main + # test process are already imported. So this disables the + # warning which will trigger to say they can no longer be + # re-written, which is fine as they are already re-written. + orig_warn = AssertionRewritingHook._warn_already_imported + + def revert(): + AssertionRewritingHook._warn_already_imported = orig_warn + + self.request.addfinalizer(revert) + AssertionRewritingHook._warn_already_imported = lambda *a: None + + rec = [] + + class Collect: + def pytest_configure(x, config): + rec.append(self.make_hook_recorder(config.pluginmanager)) + + plugins = kwargs.get("plugins") or [] + plugins.append(Collect()) + ret = pytest.main(list(args), plugins=plugins) + self.delete_loaded_modules() + if len(rec) == 1: + reprec = rec.pop() + else: + class reprec: + pass + reprec.ret = ret + + # typically we reraise keyboard interrupts from the child run + # because it's our user requesting interruption of the testing + if ret == 2 and not kwargs.get("no_reraise_ctrlc"): + calls = reprec.getcalls("pytest_keyboard_interrupt") + if calls and calls[-1].excinfo.type == KeyboardInterrupt: + raise KeyboardInterrupt() + return reprec + + def runpytest_inprocess(self, *args, **kwargs): + """ Return result of running pytest in-process, providing a similar + interface to what self.runpytest() provides. """ + if kwargs.get("syspathinsert"): + self.syspathinsert() + now = time.time() + capture = MultiCapture(Capture=SysCapture) + capture.start_capturing() + try: + try: + reprec = self.inline_run(*args, **kwargs) + except SystemExit as e: + + class reprec: + ret = e.args[0] + + except Exception: + traceback.print_exc() + + class reprec: + ret = 3 + finally: + out, err = capture.readouterr() + capture.stop_capturing() + sys.stdout.write(out) + sys.stderr.write(err) + + res = RunResult(reprec.ret, + out.split("\n"), err.split("\n"), + time.time()-now) + res.reprec = reprec + return res + + def runpytest(self, *args, **kwargs): + """ Run pytest inline or in a subprocess, depending on the command line + option "--runpytest" and return a :py:class:`RunResult`. + + """ + args = self._ensure_basetemp(args) + return self._runpytest_method(*args, **kwargs) + + def _ensure_basetemp(self, args): + args = [str(x) for x in args] + for x in args: + if str(x).startswith('--basetemp'): + #print ("basedtemp exists: %s" %(args,)) + break + else: + args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp')) + #print ("added basetemp: %s" %(args,)) + return args + + def parseconfig(self, *args): + """Return a new pytest Config instance from given commandline args. + + This invokes the pytest bootstrapping code in _pytest.config + to create a new :py:class:`_pytest.core.PluginManager` and + call the pytest_cmdline_parse hook to create new + :py:class:`_pytest.config.Config` instance. + + If :py:attr:`plugins` has been populated they should be plugin + modules which will be registered with the PluginManager. + + """ + args = self._ensure_basetemp(args) + + import _pytest.config + config = _pytest.config._prepareconfig(args, self.plugins) + # we don't know what the test will do with this half-setup config + # object and thus we make sure it gets unconfigured properly in any + # case (otherwise capturing could still be active, for example) + self.request.addfinalizer(config._ensure_unconfigure) + return config + + def parseconfigure(self, *args): + """Return a new pytest configured Config instance. + + This returns a new :py:class:`_pytest.config.Config` instance + like :py:meth:`parseconfig`, but also calls the + pytest_configure hook. + + """ + config = self.parseconfig(*args) + config._do_configure() + self.request.addfinalizer(config._ensure_unconfigure) + return config + + def getitem(self, source, funcname="test_func"): + """Return the test item for a test function. + + This writes the source to a python file and runs pytest's + collection on the resulting module, returning the test item + for the requested function name. + + :param source: The module source. + + :param funcname: The name of the test function for which the + Item must be returned. + + """ + items = self.getitems(source) + for item in items: + if item.name == funcname: + return item + assert 0, "%r item not found in module:\n%s\nitems: %s" %( + funcname, source, items) + + def getitems(self, source): + """Return all test items collected from the module. + + This writes the source to a python file and runs pytest's + collection on the resulting module, returning all test items + contained within. + + """ + modcol = self.getmodulecol(source) + return self.genitems([modcol]) + + def getmodulecol(self, source, configargs=(), withinit=False): + """Return the module collection node for ``source``. + + This writes ``source`` to a file using :py:meth:`makepyfile` + and then runs the pytest collection on it, returning the + collection node for the test module. + + :param source: The source code of the module to collect. + + :param configargs: Any extra arguments to pass to + :py:meth:`parseconfigure`. + + :param withinit: Whether to also write a ``__init__.py`` file + to the temporary directory to ensure it is a package. + + """ + kw = {self.request.function.__name__: Source(source).strip()} + path = self.makepyfile(**kw) + if withinit: + self.makepyfile(__init__ = "#") + self.config = config = self.parseconfigure(path, *configargs) + node = self.getnode(config, path) + + return node + + def collect_by_name(self, modcol, name): + """Return the collection node for name from the module collection. + + This will search a module collection node for a collection + node matching the given name. + + :param modcol: A module collection node, see + :py:meth:`getmodulecol`. + + :param name: The name of the node to return. + + """ + if modcol not in self._mod_collections: + self._mod_collections[modcol] = list(modcol.collect()) + for colitem in self._mod_collections[modcol]: + if colitem.name == name: + return colitem + + def popen(self, cmdargs, stdout, stderr, **kw): + """Invoke subprocess.Popen. + + This calls subprocess.Popen making sure the current working + directory is the PYTHONPATH. + + You probably want to use :py:meth:`run` instead. + + """ + env = os.environ.copy() + env['PYTHONPATH'] = os.pathsep.join(filter(None, [ + str(os.getcwd()), env.get('PYTHONPATH', '')])) + kw['env'] = env + return subprocess.Popen(cmdargs, + stdout=stdout, stderr=stderr, **kw) + + def run(self, *cmdargs): + """Run a command with arguments. + + Run a process using subprocess.Popen saving the stdout and + stderr. + + Returns a :py:class:`RunResult`. + + """ + return self._run(*cmdargs) + + def _run(self, *cmdargs): + cmdargs = [str(x) for x in cmdargs] + p1 = self.tmpdir.join("stdout") + p2 = self.tmpdir.join("stderr") + print("running:", ' '.join(cmdargs)) + print(" in:", str(py.path.local())) + f1 = codecs.open(str(p1), "w", encoding="utf8") + f2 = codecs.open(str(p2), "w", encoding="utf8") + try: + now = time.time() + popen = self.popen(cmdargs, stdout=f1, stderr=f2, + close_fds=(sys.platform != "win32")) + ret = popen.wait() + finally: + f1.close() + f2.close() + f1 = codecs.open(str(p1), "r", encoding="utf8") + f2 = codecs.open(str(p2), "r", encoding="utf8") + try: + out = f1.read().splitlines() + err = f2.read().splitlines() + finally: + f1.close() + f2.close() + self._dump_lines(out, sys.stdout) + self._dump_lines(err, sys.stderr) + return RunResult(ret, out, err, time.time()-now) + + def _dump_lines(self, lines, fp): + try: + for line in lines: + print(line, file=fp) + except UnicodeEncodeError: + print("couldn't print to %s because of encoding" % (fp,)) + + def _getpytestargs(self): + # we cannot use "(sys.executable,script)" + # because on windows the script is e.g. a pytest.exe + return (sys.executable, _pytest_fullpath,) # noqa + + def runpython(self, script): + """Run a python script using sys.executable as interpreter. + + Returns a :py:class:`RunResult`. + """ + return self.run(sys.executable, script) + + def runpython_c(self, command): + """Run python -c "command", return a :py:class:`RunResult`.""" + return self.run(sys.executable, "-c", command) + + def runpytest_subprocess(self, *args, **kwargs): + """Run pytest as a subprocess with given arguments. + + Any plugins added to the :py:attr:`plugins` list will added + using the ``-p`` command line option. Addtionally + ``--basetemp`` is used put any temporary files and directories + in a numbered directory prefixed with "runpytest-" so they do + not conflict with the normal numberd pytest location for + temporary files and directories. + + Returns a :py:class:`RunResult`. + + """ + p = py.path.local.make_numbered_dir(prefix="runpytest-", + keep=None, rootdir=self.tmpdir) + args = ('--basetemp=%s' % p, ) + args + #for x in args: + # if '--confcutdir' in str(x): + # break + #else: + # pass + # args = ('--confcutdir=.',) + args + plugins = [x for x in self.plugins if isinstance(x, str)] + if plugins: + args = ('-p', plugins[0]) + args + args = self._getpytestargs() + args + return self.run(*args) + + def spawn_pytest(self, string, expect_timeout=10.0): + """Run pytest using pexpect. + + This makes sure to use the right pytest and sets up the + temporary directory locations. + + The pexpect child is returned. + + """ + basetemp = self.tmpdir.mkdir("temp-pexpect") + invoke = " ".join(map(str, self._getpytestargs())) + cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string) + return self.spawn(cmd, expect_timeout=expect_timeout) + + def spawn(self, cmd, expect_timeout=10.0): + """Run a command using pexpect. + + The pexpect child is returned. + """ + pexpect = pytest.importorskip("pexpect", "3.0") + if hasattr(sys, 'pypy_version_info') and '64' in platform.machine(): + pytest.skip("pypy-64 bit not supported") + if sys.platform.startswith("freebsd"): + pytest.xfail("pexpect does not work reliably on freebsd") + logfile = self.tmpdir.join("spawn.out").open("wb") + child = pexpect.spawn(cmd, logfile=logfile) + self.request.addfinalizer(logfile.close) + child.timeout = expect_timeout + return child + +def getdecoded(out): + try: + return out.decode("utf-8") + except UnicodeDecodeError: + return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % ( + py.io.saferepr(out),) + + +class LineComp: + def __init__(self): + self.stringio = py.io.TextIO() + + def assert_contains_lines(self, lines2): + """ assert that lines2 are contained (linearly) in lines1. + return a list of extralines found. + """ + __tracebackhide__ = True + val = self.stringio.getvalue() + self.stringio.truncate(0) + self.stringio.seek(0) + lines1 = val.split("\n") + return LineMatcher(lines1).fnmatch_lines(lines2) + + +class LineMatcher: + """Flexible matching of text. + + This is a convenience class to test large texts like the output of + commands. + + The constructor takes a list of lines without their trailing + newlines, i.e. ``text.splitlines()``. + + """ + + def __init__(self, lines): + self.lines = lines + self._log_output = [] + + def str(self): + """Return the entire original text.""" + return "\n".join(self.lines) + + def _getlines(self, lines2): + if isinstance(lines2, str): + lines2 = Source(lines2) + if isinstance(lines2, Source): + lines2 = lines2.strip().lines + return lines2 + + def fnmatch_lines_random(self, lines2): + """Check lines exist in the output. + + The argument is a list of lines which have to occur in the + output, in any order. Each line can contain glob whildcards. + + """ + lines2 = self._getlines(lines2) + for line in lines2: + for x in self.lines: + if line == x or fnmatch(x, line): + self._log("matched: ", repr(line)) + break + else: + self._log("line %r not found in output" % line) + raise ValueError(self._log_text) + + def get_lines_after(self, fnline): + """Return all lines following the given line in the text. + + The given line can contain glob wildcards. + """ + for i, line in enumerate(self.lines): + if fnline == line or fnmatch(line, fnline): + return self.lines[i+1:] + raise ValueError("line %r not found in output" % fnline) + + def _log(self, *args): + self._log_output.append(' '.join((str(x) for x in args))) + + @property + def _log_text(self): + return '\n'.join(self._log_output) + + def fnmatch_lines(self, lines2): + """Search the text for matching lines. + + The argument is a list of lines which have to match and can + use glob wildcards. If they do not match an pytest.fail() is + called. The matches and non-matches are also printed on + stdout. + + """ + lines2 = self._getlines(lines2) + lines1 = self.lines[:] + nextline = None + extralines = [] + __tracebackhide__ = True + for line in lines2: + nomatchprinted = False + while lines1: + nextline = lines1.pop(0) + if line == nextline: + self._log("exact match:", repr(line)) + break + elif fnmatch(nextline, line): + self._log("fnmatch:", repr(line)) + self._log(" with:", repr(nextline)) + break + else: + if not nomatchprinted: + self._log("nomatch:", repr(line)) + nomatchprinted = True + self._log(" and:", repr(nextline)) + extralines.append(nextline) + else: + self._log("remains unmatched: %r" % (line,)) + pytest.fail(self._log_text) diff --git a/venv/lib/python3.5/site-packages/_pytest/python.py b/venv/lib/python3.5/site-packages/_pytest/python.py new file mode 100644 index 0000000..06f74ce --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/python.py @@ -0,0 +1,1597 @@ +""" Python test discovery, setup and run of test functions. """ +from __future__ import absolute_import, division, print_function + +import fnmatch +import inspect +import sys +import os +import collections +import math +from itertools import count + +import py +from _pytest.mark import MarkerError +from _pytest.config import hookimpl + +import _pytest +import _pytest._pluggy as pluggy +from _pytest import fixtures +from _pytest import main +from _pytest.compat import ( + isclass, isfunction, is_generator, _escape_strings, + REGEX_TYPE, STRING_TYPES, NoneType, NOTSET, + get_real_func, getfslineno, safe_getattr, + safe_str, getlocation, enum, +) +from _pytest.runner import fail + +cutdir1 = py.path.local(pluggy.__file__.rstrip("oc")) +cutdir2 = py.path.local(_pytest.__file__).dirpath() +cutdir3 = py.path.local(py.__file__).dirpath() + + +def filter_traceback(entry): + """Return True if a TracebackEntry instance should be removed from tracebacks: + * dynamically generated code (no code to show up for it); + * internal traceback from pytest or its internal libraries, py and pluggy. + """ + # entry.path might sometimes return a str object when the entry + # points to dynamically generated code + # see https://bitbucket.org/pytest-dev/py/issues/71 + raw_filename = entry.frame.code.raw.co_filename + is_generated = '<' in raw_filename and '>' in raw_filename + if is_generated: + return False + # entry.path might point to an inexisting file, in which case it will + # alsso return a str object. see #1133 + p = py.path.local(entry.path) + return p != cutdir1 and not p.relto(cutdir2) and not p.relto(cutdir3) + + + +def pyobj_property(name): + def get(self): + node = self.getparent(getattr(__import__('pytest'), name)) + if node is not None: + return node.obj + doc = "python %s object this node was collected from (can be None)." % ( + name.lower(),) + return property(get, None, None, doc) + + +def pytest_addoption(parser): + group = parser.getgroup("general") + group.addoption('--fixtures', '--funcargs', + action="store_true", dest="showfixtures", default=False, + help="show available fixtures, sorted by plugin appearance") + group.addoption( + '--fixtures-per-test', + action="store_true", + dest="show_fixtures_per_test", + default=False, + help="show fixtures per test", + ) + parser.addini("usefixtures", type="args", default=[], + help="list of default fixtures to be used with this project") + parser.addini("python_files", type="args", + default=['test_*.py', '*_test.py'], + help="glob-style file patterns for Python test module discovery") + parser.addini("python_classes", type="args", default=["Test",], + help="prefixes or glob names for Python test class discovery") + parser.addini("python_functions", type="args", default=["test",], + help="prefixes or glob names for Python test function and " + "method discovery") + + group.addoption("--import-mode", default="prepend", + choices=["prepend", "append"], dest="importmode", + help="prepend/append to sys.path when importing test modules, " + "default is to prepend.") + + +def pytest_cmdline_main(config): + if config.option.showfixtures: + showfixtures(config) + return 0 + if config.option.show_fixtures_per_test: + show_fixtures_per_test(config) + return 0 + + +def pytest_generate_tests(metafunc): + # those alternative spellings are common - raise a specific error to alert + # the user + alt_spellings = ['parameterize', 'parametrise', 'parameterise'] + for attr in alt_spellings: + if hasattr(metafunc.function, attr): + msg = "{0} has '{1}', spelling should be 'parametrize'" + raise MarkerError(msg.format(metafunc.function.__name__, attr)) + try: + markers = metafunc.function.parametrize + except AttributeError: + return + for marker in markers: + metafunc.parametrize(*marker.args, **marker.kwargs) + +def pytest_configure(config): + config.addinivalue_line("markers", + "parametrize(argnames, argvalues): call a test function multiple " + "times passing in different arguments in turn. argvalues generally " + "needs to be a list of values if argnames specifies only one name " + "or a list of tuples of values if argnames specifies multiple names. " + "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " + "decorated test function, one with arg1=1 and another with arg1=2." + "see http://pytest.org/latest/parametrize.html for more info and " + "examples." + ) + config.addinivalue_line("markers", + "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " + "all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures " + ) + + +@hookimpl(trylast=True) +def pytest_pyfunc_call(pyfuncitem): + testfunction = pyfuncitem.obj + if pyfuncitem._isyieldedfunction(): + testfunction(*pyfuncitem._args) + else: + funcargs = pyfuncitem.funcargs + testargs = {} + for arg in pyfuncitem._fixtureinfo.argnames: + testargs[arg] = funcargs[arg] + testfunction(**testargs) + return True + + +def pytest_collect_file(path, parent): + ext = path.ext + if ext == ".py": + if not parent.session.isinitpath(path): + for pat in parent.config.getini('python_files'): + if path.fnmatch(pat): + break + else: + return + ihook = parent.session.gethookproxy(path) + return ihook.pytest_pycollect_makemodule(path=path, parent=parent) + +def pytest_pycollect_makemodule(path, parent): + return Module(path, parent) + +@hookimpl(hookwrapper=True) +def pytest_pycollect_makeitem(collector, name, obj): + outcome = yield + res = outcome.get_result() + if res is not None: + return + # nothing was collected elsewhere, let's do it here + if isclass(obj): + if collector.istestclass(obj, name): + Class = collector._getcustomclass("Class") + outcome.force_result(Class(name, parent=collector)) + elif collector.istestfunction(obj, name): + # mock seems to store unbound methods (issue473), normalize it + obj = getattr(obj, "__func__", obj) + # We need to try and unwrap the function if it's a functools.partial + # or a funtools.wrapped. + # We musn't if it's been wrapped with mock.patch (python 2 only) + if not (isfunction(obj) or isfunction(get_real_func(obj))): + collector.warn(code="C2", message= + "cannot collect %r because it is not a function." + % name, ) + elif getattr(obj, "__test__", True): + if is_generator(obj): + res = Generator(name, parent=collector) + else: + res = list(collector._genfunctions(name, obj)) + outcome.force_result(res) + +def pytest_make_parametrize_id(config, val, argname=None): + return None + + + +class PyobjContext(object): + module = pyobj_property("Module") + cls = pyobj_property("Class") + instance = pyobj_property("Instance") + +class PyobjMixin(PyobjContext): + def obj(): + def fget(self): + obj = getattr(self, '_obj', None) + if obj is None: + self._obj = obj = self._getobj() + return obj + + def fset(self, value): + self._obj = value + + return property(fget, fset, None, "underlying python object") + + obj = obj() + + def _getobj(self): + return getattr(self.parent.obj, self.name) + + def getmodpath(self, stopatmodule=True, includemodule=False): + """ return python path relative to the containing module. """ + chain = self.listchain() + chain.reverse() + parts = [] + for node in chain: + if isinstance(node, Instance): + continue + name = node.name + if isinstance(node, Module): + name = os.path.splitext(name)[0] + if stopatmodule: + if includemodule: + parts.append(name) + break + parts.append(name) + parts.reverse() + s = ".".join(parts) + return s.replace(".[", "[") + + def _getfslineno(self): + return getfslineno(self.obj) + + def reportinfo(self): + # XXX caching? + obj = self.obj + compat_co_firstlineno = getattr(obj, 'compat_co_firstlineno', None) + if isinstance(compat_co_firstlineno, int): + # nose compatibility + fspath = sys.modules[obj.__module__].__file__ + if fspath.endswith(".pyc"): + fspath = fspath[:-1] + lineno = compat_co_firstlineno + else: + fspath, lineno = getfslineno(obj) + modpath = self.getmodpath() + assert isinstance(lineno, int) + return fspath, lineno, modpath + +class PyCollector(PyobjMixin, main.Collector): + + def funcnamefilter(self, name): + return self._matches_prefix_or_glob_option('python_functions', name) + + def isnosetest(self, obj): + """ Look for the __test__ attribute, which is applied by the + @nose.tools.istest decorator + """ + # We explicitly check for "is True" here to not mistakenly treat + # classes with a custom __getattr__ returning something truthy (like a + # function) as test classes. + return safe_getattr(obj, '__test__', False) is True + + def classnamefilter(self, name): + return self._matches_prefix_or_glob_option('python_classes', name) + + def istestfunction(self, obj, name): + return ( + (self.funcnamefilter(name) or self.isnosetest(obj)) and + safe_getattr(obj, "__call__", False) and fixtures.getfixturemarker(obj) is None + ) + + def istestclass(self, obj, name): + return self.classnamefilter(name) or self.isnosetest(obj) + + def _matches_prefix_or_glob_option(self, option_name, name): + """ + checks if the given name matches the prefix or glob-pattern defined + in ini configuration. + """ + for option in self.config.getini(option_name): + if name.startswith(option): + return True + # check that name looks like a glob-string before calling fnmatch + # because this is called for every name in each collected module, + # and fnmatch is somewhat expensive to call + elif ('*' in option or '?' in option or '[' in option) and \ + fnmatch.fnmatch(name, option): + return True + return False + + def collect(self): + if not getattr(self.obj, "__test__", True): + return [] + + # NB. we avoid random getattrs and peek in the __dict__ instead + # (XXX originally introduced from a PyPy need, still true?) + dicts = [getattr(self.obj, '__dict__', {})] + for basecls in inspect.getmro(self.obj.__class__): + dicts.append(basecls.__dict__) + seen = {} + l = [] + for dic in dicts: + for name, obj in list(dic.items()): + if name in seen: + continue + seen[name] = True + res = self.makeitem(name, obj) + if res is None: + continue + if not isinstance(res, list): + res = [res] + l.extend(res) + l.sort(key=lambda item: item.reportinfo()[:2]) + return l + + def makeitem(self, name, obj): + #assert self.ihook.fspath == self.fspath, self + return self.ihook.pytest_pycollect_makeitem( + collector=self, name=name, obj=obj) + + def _genfunctions(self, name, funcobj): + module = self.getparent(Module).obj + clscol = self.getparent(Class) + cls = clscol and clscol.obj or None + transfer_markers(funcobj, cls, module) + fm = self.session._fixturemanager + fixtureinfo = fm.getfixtureinfo(self, funcobj, cls) + metafunc = Metafunc(funcobj, fixtureinfo, self.config, + cls=cls, module=module) + methods = [] + if hasattr(module, "pytest_generate_tests"): + methods.append(module.pytest_generate_tests) + if hasattr(cls, "pytest_generate_tests"): + methods.append(cls().pytest_generate_tests) + if methods: + self.ihook.pytest_generate_tests.call_extra(methods, + dict(metafunc=metafunc)) + else: + self.ihook.pytest_generate_tests(metafunc=metafunc) + + Function = self._getcustomclass("Function") + if not metafunc._calls: + yield Function(name, parent=self, fixtureinfo=fixtureinfo) + else: + # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs + fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm) + + for callspec in metafunc._calls: + subname = "%s[%s]" % (name, callspec.id) + yield Function(name=subname, parent=self, + callspec=callspec, callobj=funcobj, + fixtureinfo=fixtureinfo, + keywords={callspec.id:True}, + originalname=name, + ) + + +def _marked(func, mark): + """ Returns True if :func: is already marked with :mark:, False otherwise. + This can happen if marker is applied to class and the test file is + invoked more than once. + """ + try: + func_mark = getattr(func, mark.name) + except AttributeError: + return False + return mark.args == func_mark.args and mark.kwargs == func_mark.kwargs + + +def transfer_markers(funcobj, cls, mod): + # XXX this should rather be code in the mark plugin or the mark + # plugin should merge with the python plugin. + for holder in (cls, mod): + try: + pytestmark = holder.pytestmark + except AttributeError: + continue + if isinstance(pytestmark, list): + for mark in pytestmark: + if not _marked(funcobj, mark): + mark(funcobj) + else: + if not _marked(funcobj, pytestmark): + pytestmark(funcobj) + + +class Module(main.File, PyCollector): + """ Collector for test classes and functions. """ + + def _getobj(self): + return self._importtestmodule() + + def collect(self): + self.session._fixturemanager.parsefactories(self) + return super(Module, self).collect() + + def _importtestmodule(self): + # we assume we are only called once per module + importmode = self.config.getoption("--import-mode") + try: + mod = self.fspath.pyimport(ensuresyspath=importmode) + except SyntaxError: + raise self.CollectError( + _pytest._code.ExceptionInfo().getrepr(style="short")) + except self.fspath.ImportMismatchError: + e = sys.exc_info()[1] + raise self.CollectError( + "import file mismatch:\n" + "imported module %r has this __file__ attribute:\n" + " %s\n" + "which is not the same as the test file we want to collect:\n" + " %s\n" + "HINT: remove __pycache__ / .pyc files and/or use a " + "unique basename for your test file modules" + % e.args + ) + except ImportError: + from _pytest._code.code import ExceptionInfo + exc_info = ExceptionInfo() + if self.config.getoption('verbose') < 2: + exc_info.traceback = exc_info.traceback.filter(filter_traceback) + exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly() + formatted_tb = safe_str(exc_repr) + raise self.CollectError( + "ImportError while importing test module '{fspath}'.\n" + "Hint: make sure your test modules/packages have valid Python names.\n" + "Traceback:\n" + "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) + ) + except _pytest.runner.Skipped as e: + if e.allow_module_level: + raise + raise self.CollectError( + "Using pytest.skip outside of a test is not allowed. If you are " + "trying to decorate a test function, use the @pytest.mark.skip " + "or @pytest.mark.skipif decorators instead." + ) + self.config.pluginmanager.consider_module(mod) + return mod + + def setup(self): + setup_module = _get_xunit_setup_teardown(self.obj, "setUpModule") + if setup_module is None: + setup_module = _get_xunit_setup_teardown(self.obj, "setup_module") + if setup_module is not None: + setup_module() + + teardown_module = _get_xunit_setup_teardown(self.obj, 'tearDownModule') + if teardown_module is None: + teardown_module = _get_xunit_setup_teardown(self.obj, 'teardown_module') + if teardown_module is not None: + self.addfinalizer(teardown_module) + + +def _get_xunit_setup_teardown(holder, attr_name, param_obj=None): + """ + Return a callable to perform xunit-style setup or teardown if + the function exists in the ``holder`` object. + The ``param_obj`` parameter is the parameter which will be passed to the function + when the callable is called without arguments, defaults to the ``holder`` object. + Return ``None`` if a suitable callable is not found. + """ + param_obj = param_obj if param_obj is not None else holder + result = _get_xunit_func(holder, attr_name) + if result is not None: + arg_count = result.__code__.co_argcount + if inspect.ismethod(result): + arg_count -= 1 + if arg_count: + return lambda: result(param_obj) + else: + return result + + +def _get_xunit_func(obj, name): + """Return the attribute from the given object to be used as a setup/teardown + xunit-style function, but only if not marked as a fixture to + avoid calling it twice. + """ + meth = getattr(obj, name, None) + if fixtures.getfixturemarker(meth) is None: + return meth + + +class Class(PyCollector): + """ Collector for test methods. """ + def collect(self): + if not safe_getattr(self.obj, "__test__", True): + return [] + if hasinit(self.obj): + self.warn("C1", "cannot collect test class %r because it has a " + "__init__ constructor" % self.obj.__name__) + return [] + elif hasnew(self.obj): + self.warn("C1", "cannot collect test class %r because it has a " + "__new__ constructor" % self.obj.__name__) + return [] + return [self._getcustomclass("Instance")(name="()", parent=self)] + + def setup(self): + setup_class = _get_xunit_func(self.obj, 'setup_class') + if setup_class is not None: + setup_class = getattr(setup_class, 'im_func', setup_class) + setup_class = getattr(setup_class, '__func__', setup_class) + setup_class(self.obj) + + fin_class = getattr(self.obj, 'teardown_class', None) + if fin_class is not None: + fin_class = getattr(fin_class, 'im_func', fin_class) + fin_class = getattr(fin_class, '__func__', fin_class) + self.addfinalizer(lambda: fin_class(self.obj)) + +class Instance(PyCollector): + def _getobj(self): + return self.parent.obj() + + def collect(self): + self.session._fixturemanager.parsefactories(self) + return super(Instance, self).collect() + + def newinstance(self): + self.obj = self._getobj() + return self.obj + +class FunctionMixin(PyobjMixin): + """ mixin for the code common to Function and Generator. + """ + + def setup(self): + """ perform setup for this test function. """ + if hasattr(self, '_preservedparent'): + obj = self._preservedparent + elif isinstance(self.parent, Instance): + obj = self.parent.newinstance() + self.obj = self._getobj() + else: + obj = self.parent.obj + if inspect.ismethod(self.obj): + setup_name = 'setup_method' + teardown_name = 'teardown_method' + else: + setup_name = 'setup_function' + teardown_name = 'teardown_function' + setup_func_or_method = _get_xunit_setup_teardown(obj, setup_name, param_obj=self.obj) + if setup_func_or_method is not None: + setup_func_or_method() + teardown_func_or_method = _get_xunit_setup_teardown(obj, teardown_name, param_obj=self.obj) + if teardown_func_or_method is not None: + self.addfinalizer(teardown_func_or_method) + + def _prunetraceback(self, excinfo): + if hasattr(self, '_obj') and not self.config.option.fulltrace: + code = _pytest._code.Code(get_real_func(self.obj)) + path, firstlineno = code.path, code.firstlineno + traceback = excinfo.traceback + ntraceback = traceback.cut(path=path, firstlineno=firstlineno) + if ntraceback == traceback: + ntraceback = ntraceback.cut(path=path) + if ntraceback == traceback: + #ntraceback = ntraceback.cut(excludepath=cutdir2) + ntraceback = ntraceback.filter(filter_traceback) + if not ntraceback: + ntraceback = traceback + + excinfo.traceback = ntraceback.filter() + # issue364: mark all but first and last frames to + # only show a single-line message for each frame + if self.config.option.tbstyle == "auto": + if len(excinfo.traceback) > 2: + for entry in excinfo.traceback[1:-1]: + entry.set_repr_style('short') + + def _repr_failure_py(self, excinfo, style="long"): + if excinfo.errisinstance(fail.Exception): + if not excinfo.value.pytrace: + return py._builtin._totext(excinfo.value) + return super(FunctionMixin, self)._repr_failure_py(excinfo, + style=style) + + def repr_failure(self, excinfo, outerr=None): + assert outerr is None, "XXX outerr usage is deprecated" + style = self.config.option.tbstyle + if style == "auto": + style = "long" + return self._repr_failure_py(excinfo, style=style) + + +class Generator(FunctionMixin, PyCollector): + def collect(self): + # test generators are seen as collectors but they also + # invoke setup/teardown on popular request + # (induced by the common "test_*" naming shared with normal tests) + from _pytest import deprecated + self.session._setupstate.prepare(self) + # see FunctionMixin.setup and test_setupstate_is_preserved_134 + self._preservedparent = self.parent.obj + l = [] + seen = {} + for i, x in enumerate(self.obj()): + name, call, args = self.getcallargs(x) + if not callable(call): + raise TypeError("%r yielded non callable test %r" %(self.obj, call,)) + if name is None: + name = "[%d]" % i + else: + name = "['%s']" % name + if name in seen: + raise ValueError("%r generated tests with non-unique name %r" %(self, name)) + seen[name] = True + l.append(self.Function(name, self, args=args, callobj=call)) + self.config.warn('C1', deprecated.YIELD_TESTS, fslocation=self.fspath) + return l + + def getcallargs(self, obj): + if not isinstance(obj, (tuple, list)): + obj = (obj,) + # explicit naming + if isinstance(obj[0], py.builtin._basestring): + name = obj[0] + obj = obj[1:] + else: + name = None + call, args = obj[0], obj[1:] + return name, call, args + + +def hasinit(obj): + init = getattr(obj, '__init__', None) + if init: + return init != object.__init__ + + +def hasnew(obj): + new = getattr(obj, '__new__', None) + if new: + return new != object.__new__ + + +class CallSpec2(object): + def __init__(self, metafunc): + self.metafunc = metafunc + self.funcargs = {} + self._idlist = [] + self.params = {} + self._globalid = NOTSET + self._globalid_args = set() + self._globalparam = NOTSET + self._arg2scopenum = {} # used for sorting parametrized resources + self.keywords = {} + self.indices = {} + + def copy(self, metafunc): + cs = CallSpec2(self.metafunc) + cs.funcargs.update(self.funcargs) + cs.params.update(self.params) + cs.keywords.update(self.keywords) + cs.indices.update(self.indices) + cs._arg2scopenum.update(self._arg2scopenum) + cs._idlist = list(self._idlist) + cs._globalid = self._globalid + cs._globalid_args = self._globalid_args + cs._globalparam = self._globalparam + return cs + + def _checkargnotcontained(self, arg): + if arg in self.params or arg in self.funcargs: + raise ValueError("duplicate %r" %(arg,)) + + def getparam(self, name): + try: + return self.params[name] + except KeyError: + if self._globalparam is NOTSET: + raise ValueError(name) + return self._globalparam + + @property + def id(self): + return "-".join(map(str, filter(None, self._idlist))) + + def setmulti(self, valtypes, argnames, valset, id, keywords, scopenum, + param_index): + for arg,val in zip(argnames, valset): + self._checkargnotcontained(arg) + valtype_for_arg = valtypes[arg] + getattr(self, valtype_for_arg)[arg] = val + self.indices[arg] = param_index + self._arg2scopenum[arg] = scopenum + self._idlist.append(id) + self.keywords.update(keywords) + + def setall(self, funcargs, id, param): + for x in funcargs: + self._checkargnotcontained(x) + self.funcargs.update(funcargs) + if id is not NOTSET: + self._idlist.append(id) + if param is not NOTSET: + assert self._globalparam is NOTSET + self._globalparam = param + for arg in funcargs: + self._arg2scopenum[arg] = fixtures.scopenum_function + + +class Metafunc(fixtures.FuncargnamesCompatAttr): + """ + Metafunc objects are passed to the ``pytest_generate_tests`` hook. + They help to inspect a test function and to generate tests according to + test configuration or values specified in the class or module where a + test function is defined. + """ + def __init__(self, function, fixtureinfo, config, cls=None, module=None): + #: access to the :class:`_pytest.config.Config` object for the test session + self.config = config + + #: the module object where the test function is defined in. + self.module = module + + #: underlying python test function + self.function = function + + #: set of fixture names required by the test function + self.fixturenames = fixtureinfo.names_closure + + #: class object where the test function is defined in or ``None``. + self.cls = cls + + self._calls = [] + self._ids = py.builtin.set() + self._arg2fixturedefs = fixtureinfo.name2fixturedefs + + def parametrize(self, argnames, argvalues, indirect=False, ids=None, + scope=None): + """ Add new invocations to the underlying test function using the list + of argvalues for the given argnames. Parametrization is performed + during the collection phase. If you need to setup expensive resources + see about setting indirect to do it rather at test setup time. + + :arg argnames: a comma-separated string denoting one or more argument + names, or a list/tuple of argument strings. + + :arg argvalues: The list of argvalues determines how often a + test is invoked with different argument values. If only one + argname was specified argvalues is a list of values. If N + argnames were specified, argvalues must be a list of N-tuples, + where each tuple-element specifies a value for its respective + argname. + + :arg indirect: The list of argnames or boolean. A list of arguments' + names (subset of argnames). If True the list contains all names from + the argnames. Each argvalue corresponding to an argname in this list will + be passed as request.param to its respective argname fixture + function so that it can perform more expensive setups during the + setup phase of a test rather than at collection time. + + :arg ids: list of string ids, or a callable. + If strings, each is corresponding to the argvalues so that they are + part of the test id. If None is given as id of specific test, the + automatically generated id for that argument will be used. + If callable, it should take one argument (a single argvalue) and return + a string or return None. If None, the automatically generated id for that + argument will be used. + If no ids are provided they will be generated automatically from + the argvalues. + + :arg scope: if specified it denotes the scope of the parameters. + The scope is used for grouping tests by parameter instances. + It will also override any fixture-function defined scope, allowing + to set a dynamic scope using test context or configuration. + """ + from _pytest.fixtures import scope2index + from _pytest.mark import MARK_GEN, ParameterSet + from py.io import saferepr + + if not isinstance(argnames, (tuple, list)): + argnames = [x.strip() for x in argnames.split(",") if x.strip()] + force_tuple = len(argnames) == 1 + else: + force_tuple = False + parameters = [ + ParameterSet.extract_from(x, legacy_force_tuple=force_tuple) + for x in argvalues] + del argvalues + + if not parameters: + fs, lineno = getfslineno(self.function) + reason = "got empty parameter set %r, function %s at %s:%d" % ( + argnames, self.function.__name__, fs, lineno) + mark = MARK_GEN.skip(reason=reason) + parameters.append(ParameterSet( + values=(NOTSET,) * len(argnames), + marks=[mark], + id=None, + )) + + if scope is None: + scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + + scopenum = scope2index(scope, descr='call to {0}'.format(self.parametrize)) + valtypes = {} + for arg in argnames: + if arg not in self.fixturenames: + if isinstance(indirect, (tuple, list)): + name = 'fixture' if arg in indirect else 'argument' + else: + name = 'fixture' if indirect else 'argument' + raise ValueError( + "%r uses no %s %r" % ( + self.function, name, arg)) + + if indirect is True: + valtypes = dict.fromkeys(argnames, "params") + elif indirect is False: + valtypes = dict.fromkeys(argnames, "funcargs") + elif isinstance(indirect, (tuple, list)): + valtypes = dict.fromkeys(argnames, "funcargs") + for arg in indirect: + if arg not in argnames: + raise ValueError("indirect given to %r: fixture %r doesn't exist" % ( + self.function, arg)) + valtypes[arg] = "params" + idfn = None + if callable(ids): + idfn = ids + ids = None + if ids: + if len(ids) != len(parameters): + raise ValueError('%d tests specified with %d ids' % ( + len(parameters), len(ids))) + for id_value in ids: + if id_value is not None and not isinstance(id_value, py.builtin._basestring): + msg = 'ids must be list of strings, found: %s (type: %s)' + raise ValueError(msg % (saferepr(id_value), type(id_value).__name__)) + ids = idmaker(argnames, parameters, idfn, ids, self.config) + newcalls = [] + for callspec in self._calls or [CallSpec2(self)]: + elements = zip(ids, parameters, count()) + for a_id, param, param_index in elements: + if len(param.values) != len(argnames): + raise ValueError( + 'In "parametrize" the number of values ({0}) must be ' + 'equal to the number of names ({1})'.format( + param.values, argnames)) + newcallspec = callspec.copy(self) + newcallspec.setmulti(valtypes, argnames, param.values, a_id, + param.deprecated_arg_dict, scopenum, param_index) + newcalls.append(newcallspec) + self._calls = newcalls + + def addcall(self, funcargs=None, id=NOTSET, param=NOTSET): + """ (deprecated, use parametrize) Add a new call to the underlying + test function during the collection phase of a test run. Note that + request.addcall() is called during the test collection phase prior and + independently to actual test execution. You should only use addcall() + if you need to specify multiple arguments of a test function. + + :arg funcargs: argument keyword dictionary used when invoking + the test function. + + :arg id: used for reporting and identification purposes. If you + don't supply an `id` an automatic unique id will be generated. + + :arg param: a parameter which will be exposed to a later fixture function + invocation through the ``request.param`` attribute. + """ + assert funcargs is None or isinstance(funcargs, dict) + if funcargs is not None: + for name in funcargs: + if name not in self.fixturenames: + fail("funcarg %r not used in this function." % name) + else: + funcargs = {} + if id is None: + raise ValueError("id=None not allowed") + if id is NOTSET: + id = len(self._calls) + id = str(id) + if id in self._ids: + raise ValueError("duplicate id %r" % id) + self._ids.add(id) + + cs = CallSpec2(self) + cs.setall(funcargs, id, param) + self._calls.append(cs) + + +def _find_parametrized_scope(argnames, arg2fixturedefs, indirect): + """Find the most appropriate scope for a parametrized call based on its arguments. + + When there's at least one direct argument, always use "function" scope. + + When a test function is parametrized and all its arguments are indirect + (e.g. fixtures), return the most narrow scope based on the fixtures used. + + Related to issue #1832, based on code posted by @Kingdread. + """ + from _pytest.fixtures import scopes + indirect_as_list = isinstance(indirect, (list, tuple)) + all_arguments_are_fixtures = indirect is True or \ + indirect_as_list and len(indirect) == argnames + if all_arguments_are_fixtures: + fixturedefs = arg2fixturedefs or {} + used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()] + if used_scopes: + # Takes the most narrow scope from used fixtures + for scope in reversed(scopes): + if scope in used_scopes: + return scope + + return 'function' + + +def _idval(val, argname, idx, idfn, config=None): + if idfn: + s = None + try: + s = idfn(val) + except Exception: + # See issue https://github.com/pytest-dev/pytest/issues/2169 + import warnings + msg = "Raised while trying to determine id of parameter %s at position %d." % (argname, idx) + msg += '\nUpdate your code as this will raise an error in pytest-4.0.' + warnings.warn(msg, DeprecationWarning) + if s: + return _escape_strings(s) + + if config: + hook_id = config.hook.pytest_make_parametrize_id( + config=config, val=val, argname=argname) + if hook_id: + return hook_id + + if isinstance(val, STRING_TYPES): + return _escape_strings(val) + elif isinstance(val, (float, int, bool, NoneType)): + return str(val) + elif isinstance(val, REGEX_TYPE): + return _escape_strings(val.pattern) + elif enum is not None and isinstance(val, enum.Enum): + return str(val) + elif isclass(val) and hasattr(val, '__name__'): + return val.__name__ + return str(argname)+str(idx) + + +def _idvalset(idx, parameterset, argnames, idfn, ids, config=None): + if parameterset.id is not None: + return parameterset.id + if ids is None or (idx >= len(ids) or ids[idx] is None): + this_id = [_idval(val, argname, idx, idfn, config) + for val, argname in zip(parameterset.values, argnames)] + return "-".join(this_id) + else: + return _escape_strings(ids[idx]) + + +def idmaker(argnames, parametersets, idfn=None, ids=None, config=None): + ids = [_idvalset(valindex, parameterset, argnames, idfn, ids, config) + for valindex, parameterset in enumerate(parametersets)] + if len(set(ids)) != len(ids): + # The ids are not unique + duplicates = [testid for testid in ids if ids.count(testid) > 1] + counters = collections.defaultdict(lambda: 0) + for index, testid in enumerate(ids): + if testid in duplicates: + ids[index] = testid + str(counters[testid]) + counters[testid] += 1 + return ids + + +def show_fixtures_per_test(config): + from _pytest.main import wrap_session + return wrap_session(config, _show_fixtures_per_test) + + +def _show_fixtures_per_test(config, session): + import _pytest.config + session.perform_collect() + curdir = py.path.local() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + def get_best_rel(func): + loc = getlocation(func, curdir) + return curdir.bestrelpath(loc) + + def write_fixture(fixture_def): + argname = fixture_def.argname + + if verbose <= 0 and argname.startswith("_"): + return + if verbose > 0: + bestrel = get_best_rel(fixture_def.func) + funcargspec = "{0} -- {1}".format(argname, bestrel) + else: + funcargspec = argname + tw.line(funcargspec, green=True) + + INDENT = ' {0}' + fixture_doc = fixture_def.func.__doc__ + + if fixture_doc: + for line in fixture_doc.strip().split('\n'): + tw.line(INDENT.format(line.strip())) + else: + tw.line(INDENT.format('no docstring available'), red=True) + + def write_item(item): + name2fixturedefs = item._fixtureinfo.name2fixturedefs + + if not name2fixturedefs: + # The given test item does not use any fixtures + return + bestrel = get_best_rel(item.function) + + tw.line() + tw.sep('-', 'fixtures used by {0}'.format(item.name)) + tw.sep('-', '({0})'.format(bestrel)) + for argname, fixture_defs in sorted(name2fixturedefs.items()): + assert fixture_defs is not None + if not fixture_defs: + continue + # The last fixture def item in the list is expected + # to be the one used by the test item + write_fixture(fixture_defs[-1]) + + for item in session.items: + write_item(item) + + +def showfixtures(config): + from _pytest.main import wrap_session + return wrap_session(config, _showfixtures_main) + + +def _showfixtures_main(config, session): + import _pytest.config + session.perform_collect() + curdir = py.path.local() + tw = _pytest.config.create_terminal_writer(config) + verbose = config.getvalue("verbose") + + fm = session._fixturemanager + + available = [] + seen = set() + + for argname, fixturedefs in fm._arg2fixturedefs.items(): + assert fixturedefs is not None + if not fixturedefs: + continue + for fixturedef in fixturedefs: + loc = getlocation(fixturedef.func, curdir) + if (fixturedef.argname, loc) in seen: + continue + seen.add((fixturedef.argname, loc)) + available.append((len(fixturedef.baseid), + fixturedef.func.__module__, + curdir.bestrelpath(loc), + fixturedef.argname, fixturedef)) + + available.sort() + currentmodule = None + for baseid, module, bestrel, argname, fixturedef in available: + if currentmodule != module: + if not module.startswith("_pytest."): + tw.line() + tw.sep("-", "fixtures defined from %s" %(module,)) + currentmodule = module + if verbose <= 0 and argname[0] == "_": + continue + if verbose > 0: + funcargspec = "%s -- %s" %(argname, bestrel,) + else: + funcargspec = argname + tw.line(funcargspec, green=True) + loc = getlocation(fixturedef.func, curdir) + doc = fixturedef.func.__doc__ or "" + if doc: + for line in doc.strip().split("\n"): + tw.line(" " + line.strip()) + else: + tw.line(" %s: no docstring available" %(loc,), + red=True) + + +# builtin pytest.raises helper + +def raises(expected_exception, *args, **kwargs): + """ + Assert that a code block/function call raises ``expected_exception`` + and raise a failure exception otherwise. + + This helper produces a ``ExceptionInfo()`` object (see below). + + If using Python 2.5 or above, you may use this function as a + context manager:: + + >>> with raises(ZeroDivisionError): + ... 1/0 + + .. versionchanged:: 2.10 + + In the context manager form you may use the keyword argument + ``message`` to specify a custom failure message:: + + >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"): + ... pass + Traceback (most recent call last): + ... + Failed: Expecting ZeroDivisionError + + + .. note:: + + When using ``pytest.raises`` as a context manager, it's worthwhile to + note that normal context manager rules apply and that the exception + raised *must* be the final line in the scope of the context manager. + Lines of code after that, within the scope of the context manager will + not be executed. For example:: + + >>> value = 15 + >>> with raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... assert exc_info.type == ValueError # this will not execute + + Instead, the following approach must be taken (note the difference in + scope):: + + >>> with raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... + >>> assert exc_info.type == ValueError + + Or you can use the keyword argument ``match`` to assert that the + exception matches a text or regex:: + + >>> with raises(ValueError, match='must be 0 or None'): + ... raise ValueError("value must be 0 or None") + + >>> with raises(ValueError, match=r'must be \d+$'): + ... raise ValueError("value must be 42") + + + Or you can specify a callable by passing a to-be-called lambda:: + + >>> raises(ZeroDivisionError, lambda: 1/0) + + + or you can specify an arbitrary callable with arguments:: + + >>> def f(x): return 1/x + ... + >>> raises(ZeroDivisionError, f, 0) + + >>> raises(ZeroDivisionError, f, x=0) + + + A third possibility is to use a string to be executed:: + + >>> raises(ZeroDivisionError, "f(0)") + + + .. autoclass:: _pytest._code.ExceptionInfo + :members: + + .. note:: + Similar to caught exception objects in Python, explicitly clearing + local references to returned ``ExceptionInfo`` objects can + help the Python interpreter speed up its garbage collection. + + Clearing those references breaks a reference cycle + (``ExceptionInfo`` --> caught exception --> frame stack raising + the exception --> current frame stack --> local variables --> + ``ExceptionInfo``) which makes Python keep all objects referenced + from that cycle (including all local variables in the current + frame) alive until the next cyclic garbage collection run. See the + official Python ``try`` statement documentation for more detailed + information. + + """ + __tracebackhide__ = True + msg = ("exceptions must be old-style classes or" + " derived from BaseException, not %s") + if isinstance(expected_exception, tuple): + for exc in expected_exception: + if not isclass(exc): + raise TypeError(msg % type(exc)) + elif not isclass(expected_exception): + raise TypeError(msg % type(expected_exception)) + + message = "DID NOT RAISE {0}".format(expected_exception) + match_expr = None + + if not args: + if "message" in kwargs: + message = kwargs.pop("message") + if "match" in kwargs: + match_expr = kwargs.pop("match") + message += " matching '{0}'".format(match_expr) + return RaisesContext(expected_exception, message, match_expr) + elif isinstance(args[0], str): + code, = args + assert isinstance(code, str) + frame = sys._getframe(1) + loc = frame.f_locals.copy() + loc.update(kwargs) + #print "raises frame scope: %r" % frame.f_locals + try: + code = _pytest._code.Source(code).compile() + py.builtin.exec_(code, frame.f_globals, loc) + # XXX didn'T mean f_globals == f_locals something special? + # this is destroyed here ... + except expected_exception: + return _pytest._code.ExceptionInfo() + else: + func = args[0] + try: + func(*args[1:], **kwargs) + except expected_exception: + return _pytest._code.ExceptionInfo() + fail(message) + + +raises.Exception = fail.Exception + + +class RaisesContext(object): + def __init__(self, expected_exception, message, match_expr): + self.expected_exception = expected_exception + self.message = message + self.match_expr = match_expr + self.excinfo = None + + def __enter__(self): + self.excinfo = object.__new__(_pytest._code.ExceptionInfo) + return self.excinfo + + def __exit__(self, *tp): + __tracebackhide__ = True + if tp[0] is None: + fail(self.message) + if sys.version_info < (2, 7): + # py26: on __exit__() exc_value often does not contain the + # exception value. + # http://bugs.python.org/issue7853 + if not isinstance(tp[1], BaseException): + exc_type, value, traceback = tp + tp = exc_type, exc_type(value), traceback + self.excinfo.__init__(tp) + suppress_exception = issubclass(self.excinfo.type, self.expected_exception) + if sys.version_info[0] == 2 and suppress_exception: + sys.exc_clear() + if self.match_expr: + self.excinfo.match(self.match_expr) + return suppress_exception + + +# builtin pytest.approx helper + +class approx(object): + """ + Assert that two numbers (or two sets of numbers) are equal to each other + within some tolerance. + + Due to the `intricacies of floating-point arithmetic`__, numbers that we + would intuitively expect to be equal are not always so:: + + >>> 0.1 + 0.2 == 0.3 + False + + __ https://docs.python.org/3/tutorial/floatingpoint.html + + This problem is commonly encountered when writing tests, e.g. when making + sure that floating-point values are what you expect them to be. One way to + deal with this problem is to assert that two floating-point numbers are + equal to within some appropriate tolerance:: + + >>> abs((0.1 + 0.2) - 0.3) < 1e-6 + True + + However, comparisons like this are tedious to write and difficult to + understand. Furthermore, absolute comparisons like the one above are + usually discouraged because there's no tolerance that works well for all + situations. ``1e-6`` is good for numbers around ``1``, but too small for + very big numbers and too big for very small ones. It's better to express + the tolerance as a fraction of the expected value, but relative comparisons + like that are even more difficult to write correctly and concisely. + + The ``approx`` class performs floating-point comparisons using a syntax + that's as intuitive as possible:: + + >>> from pytest import approx + >>> 0.1 + 0.2 == approx(0.3) + True + + The same syntax also works on sequences of numbers:: + + >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) + True + + By default, ``approx`` considers numbers within a relative tolerance of + ``1e-6`` (i.e. one part in a million) of its expected value to be equal. + This treatment would lead to surprising results if the expected value was + ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. + To handle this case less surprisingly, ``approx`` also considers numbers + within an absolute tolerance of ``1e-12`` of its expected value to be + equal. Infinite numbers are another special case. They are only + considered equal to themselves, regardless of the relative tolerance. Both + the relative and absolute tolerances can be changed by passing arguments to + the ``approx`` constructor:: + + >>> 1.0001 == approx(1) + False + >>> 1.0001 == approx(1, rel=1e-3) + True + >>> 1.0001 == approx(1, abs=1e-3) + True + + If you specify ``abs`` but not ``rel``, the comparison will not consider + the relative tolerance at all. In other words, two numbers that are within + the default relative tolerance of ``1e-6`` will still be considered unequal + if they exceed the specified absolute tolerance. If you specify both + ``abs`` and ``rel``, the numbers will be considered equal if either + tolerance is met:: + + >>> 1 + 1e-8 == approx(1) + True + >>> 1 + 1e-8 == approx(1, abs=1e-12) + False + >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) + True + + If you're thinking about using ``approx``, then you might want to know how + it compares to other good ways of comparing floating-point numbers. All of + these algorithms are based on relative and absolute tolerances and should + agree for the most part, but they do have meaningful differences: + + - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative + tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute + tolerance is met. Because the relative tolerance is calculated w.r.t. + both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor + ``b`` is a "reference value"). You have to specify an absolute tolerance + if you want to compare to ``0.0`` because there is no tolerance by + default. Only available in python>=3.5. `More information...`__ + + __ https://docs.python.org/3/library/math.html#math.isclose + + - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference + between ``a`` and ``b`` is less that the sum of the relative tolerance + w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance + is only calculated w.r.t. ``b``, this test is asymmetric and you can + think of ``b`` as the reference value. Support for comparing sequences + is provided by ``numpy.allclose``. `More information...`__ + + __ http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.isclose.html + + - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` + are within an absolute tolerance of ``1e-7``. No relative tolerance is + considered and the absolute tolerance cannot be changed, so this function + is not appropriate for very large or very small numbers. Also, it's only + available in subclasses of ``unittest.TestCase`` and it's ugly because it + doesn't follow PEP8. `More information...`__ + + __ https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual + + - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative + tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. + Because the relative tolerance is only calculated w.r.t. ``b``, this test + is asymmetric and you can think of ``b`` as the reference value. In the + special case that you explicitly specify an absolute tolerance but not a + relative tolerance, only the absolute tolerance is considered. + """ + + def __init__(self, expected, rel=None, abs=None): + self.expected = expected + self.abs = abs + self.rel = rel + + def __repr__(self): + return ', '.join(repr(x) for x in self.expected) + + def __eq__(self, actual): + from collections import Iterable + if not isinstance(actual, Iterable): + actual = [actual] + if len(actual) != len(self.expected): + return False + return all(a == x for a, x in zip(actual, self.expected)) + + __hash__ = None + + def __ne__(self, actual): + return not (actual == self) + + @property + def expected(self): + # Regardless of whether the user-specified expected value is a number + # or a sequence of numbers, return a list of ApproxNotIterable objects + # that can be compared against. + from collections import Iterable + approx_non_iter = lambda x: ApproxNonIterable(x, self.rel, self.abs) + if isinstance(self._expected, Iterable): + return [approx_non_iter(x) for x in self._expected] + else: + return [approx_non_iter(self._expected)] + + @expected.setter + def expected(self, expected): + self._expected = expected + + +class ApproxNonIterable(object): + """ + Perform approximate comparisons for single numbers only. + + In other words, the ``expected`` attribute for objects of this class must + be some sort of number. This is in contrast to the ``approx`` class, where + the ``expected`` attribute can either be a number of a sequence of numbers. + This class is responsible for making comparisons, while ``approx`` is + responsible for abstracting the difference between numbers and sequences of + numbers. Although this class can stand on its own, it's only meant to be + used within ``approx``. + """ + + def __init__(self, expected, rel=None, abs=None): + self.expected = expected + self.abs = abs + self.rel = rel + + def __repr__(self): + if isinstance(self.expected, complex): + return str(self.expected) + + # Infinities aren't compared using tolerances, so don't show a + # tolerance. + if math.isinf(self.expected): + return str(self.expected) + + # If a sensible tolerance can't be calculated, self.tolerance will + # raise a ValueError. In this case, display '???'. + try: + vetted_tolerance = '{:.1e}'.format(self.tolerance) + except ValueError: + vetted_tolerance = '???' + + if sys.version_info[0] == 2: + return '{0} +- {1}'.format(self.expected, vetted_tolerance) + else: + return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance) + + def __eq__(self, actual): + # Short-circuit exact equality. + if actual == self.expected: + return True + + # Infinity shouldn't be approximately equal to anything but itself, but + # if there's a relative tolerance, it will be infinite and infinity + # will seem approximately equal to everything. The equal-to-itself + # case would have been short circuited above, so here we can just + # return false if the expected value is infinite. The abs() call is + # for compatibility with complex numbers. + if math.isinf(abs(self.expected)): + return False + + # Return true if the two numbers are within the tolerance. + return abs(self.expected - actual) <= self.tolerance + + __hash__ = None + + def __ne__(self, actual): + return not (actual == self) + + @property + def tolerance(self): + set_default = lambda x, default: x if x is not None else default + + # Figure out what the absolute tolerance should be. ``self.abs`` is + # either None or a value specified by the user. + absolute_tolerance = set_default(self.abs, 1e-12) + + if absolute_tolerance < 0: + raise ValueError("absolute tolerance can't be negative: {}".format(absolute_tolerance)) + if math.isnan(absolute_tolerance): + raise ValueError("absolute tolerance can't be NaN.") + + # If the user specified an absolute tolerance but not a relative one, + # just return the absolute tolerance. + if self.rel is None: + if self.abs is not None: + return absolute_tolerance + + # Figure out what the relative tolerance should be. ``self.rel`` is + # either None or a value specified by the user. This is done after + # we've made sure the user didn't ask for an absolute tolerance only, + # because we don't want to raise errors about the relative tolerance if + # we aren't even going to use it. + relative_tolerance = set_default(self.rel, 1e-6) * abs(self.expected) + + if relative_tolerance < 0: + raise ValueError("relative tolerance can't be negative: {}".format(absolute_tolerance)) + if math.isnan(relative_tolerance): + raise ValueError("relative tolerance can't be NaN.") + + # Return the larger of the relative and absolute tolerances. + return max(relative_tolerance, absolute_tolerance) + + +# +# the basic pytest Function item +# + +class Function(FunctionMixin, main.Item, fixtures.FuncargnamesCompatAttr): + """ a Function Item is responsible for setting up and executing a + Python test function. + """ + _genid = None + def __init__(self, name, parent, args=None, config=None, + callspec=None, callobj=NOTSET, keywords=None, session=None, + fixtureinfo=None, originalname=None): + super(Function, self).__init__(name, parent, config=config, + session=session) + self._args = args + if callobj is not NOTSET: + self.obj = callobj + + self.keywords.update(self.obj.__dict__) + if callspec: + self.callspec = callspec + self.keywords.update(callspec.keywords) + if keywords: + self.keywords.update(keywords) + + if fixtureinfo is None: + fixtureinfo = self.session._fixturemanager.getfixtureinfo( + self.parent, self.obj, self.cls, + funcargs=not self._isyieldedfunction()) + self._fixtureinfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + #: original function name, without any decorations (for example + #: parametrization adds a ``"[...]"`` suffix to function names). + #: + #: .. versionadded:: 3.0 + self.originalname = originalname + + def _initrequest(self): + self.funcargs = {} + if self._isyieldedfunction(): + assert not hasattr(self, "callspec"), ( + "yielded functions (deprecated) cannot have funcargs") + else: + if hasattr(self, "callspec"): + callspec = self.callspec + assert not callspec.funcargs + self._genid = callspec.id + if hasattr(callspec, "param"): + self.param = callspec.param + self._request = fixtures.FixtureRequest(self) + + @property + def function(self): + "underlying python 'function' object" + return getattr(self.obj, 'im_func', self.obj) + + def _getobj(self): + name = self.name + i = name.find("[") # parametrization + if i != -1: + name = name[:i] + return getattr(self.parent.obj, name) + + @property + def _pyfuncitem(self): + "(compatonly) for code expecting pytest-2.2 style request objects" + return self + + def _isyieldedfunction(self): + return getattr(self, "_args", None) is not None + + def runtest(self): + """ execute the underlying test function. """ + self.ihook.pytest_pyfunc_call(pyfuncitem=self) + + def setup(self): + super(Function, self).setup() + fixtures.fillfixtures(self) diff --git a/venv/lib/python3.5/site-packages/_pytest/recwarn.py b/venv/lib/python3.5/site-packages/_pytest/recwarn.py new file mode 100644 index 0000000..9cc404a --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/recwarn.py @@ -0,0 +1,204 @@ +""" recording warnings during test function execution. """ +from __future__ import absolute_import, division, print_function + +import inspect + +import _pytest._code +import py +import sys +import warnings +from _pytest.fixtures import yield_fixture + + +@yield_fixture +def recwarn(): + """Return a WarningsRecorder instance that provides these methods: + + * ``pop(category=None)``: return last warning matching the category. + * ``clear()``: clear list of warnings + + See http://docs.python.org/library/warnings.html for information + on warning categories. + """ + wrec = WarningsRecorder() + with wrec: + warnings.simplefilter('default') + yield wrec + + +def deprecated_call(func=None, *args, **kwargs): + """context manager that can be used to ensure a block of code triggers a + ``DeprecationWarning`` or ``PendingDeprecationWarning``:: + + >>> import warnings + >>> def api_call_v2(): + ... warnings.warn('use v3 of this api', DeprecationWarning) + ... return 200 + + >>> with deprecated_call(): + ... assert api_call_v2() == 200 + + ``deprecated_call`` can also be used by passing a function and ``*args`` and ``*kwargs``, + in which case it will ensure calling ``func(*args, **kwargs)`` produces one of the warnings + types above. + """ + if not func: + return _DeprecatedCallContext() + else: + __tracebackhide__ = True + with _DeprecatedCallContext(): + return func(*args, **kwargs) + + +class _DeprecatedCallContext(object): + """Implements the logic to capture deprecation warnings as a context manager.""" + + def __enter__(self): + self._captured_categories = [] + self._old_warn = warnings.warn + self._old_warn_explicit = warnings.warn_explicit + warnings.warn_explicit = self._warn_explicit + warnings.warn = self._warn + + def _warn_explicit(self, message, category, *args, **kwargs): + self._captured_categories.append(category) + + def _warn(self, message, category=None, *args, **kwargs): + if isinstance(message, Warning): + self._captured_categories.append(message.__class__) + else: + self._captured_categories.append(category) + + def __exit__(self, exc_type, exc_val, exc_tb): + warnings.warn_explicit = self._old_warn_explicit + warnings.warn = self._old_warn + + if exc_type is None: + deprecation_categories = (DeprecationWarning, PendingDeprecationWarning) + if not any(issubclass(c, deprecation_categories) for c in self._captured_categories): + __tracebackhide__ = True + msg = "Did not produce DeprecationWarning or PendingDeprecationWarning" + raise AssertionError(msg) + + +def warns(expected_warning, *args, **kwargs): + """Assert that code raises a particular class of warning. + + Specifically, the input @expected_warning can be a warning class or + tuple of warning classes, and the code must return that warning + (if a single class) or one of those warnings (if a tuple). + + This helper produces a list of ``warnings.WarningMessage`` objects, + one for each warning raised. + + This function can be used as a context manager, or any of the other ways + ``pytest.raises`` can be used:: + + >>> with warns(RuntimeWarning): + ... warnings.warn("my warning", RuntimeWarning) + """ + wcheck = WarningsChecker(expected_warning) + if not args: + return wcheck + elif isinstance(args[0], str): + code, = args + assert isinstance(code, str) + frame = sys._getframe(1) + loc = frame.f_locals.copy() + loc.update(kwargs) + + with wcheck: + code = _pytest._code.Source(code).compile() + py.builtin.exec_(code, frame.f_globals, loc) + else: + func = args[0] + with wcheck: + return func(*args[1:], **kwargs) + + +class WarningsRecorder(warnings.catch_warnings): + """A context manager to record raised warnings. + + Adapted from `warnings.catch_warnings`. + """ + + def __init__(self): + super(WarningsRecorder, self).__init__(record=True) + self._entered = False + self._list = [] + + @property + def list(self): + """The list of recorded warnings.""" + return self._list + + def __getitem__(self, i): + """Get a recorded warning by index.""" + return self._list[i] + + def __iter__(self): + """Iterate through the recorded warnings.""" + return iter(self._list) + + def __len__(self): + """The number of recorded warnings.""" + return len(self._list) + + def pop(self, cls=Warning): + """Pop the first recorded warning, raise exception if not exists.""" + for i, w in enumerate(self._list): + if issubclass(w.category, cls): + return self._list.pop(i) + __tracebackhide__ = True + raise AssertionError("%r not found in warning list" % cls) + + def clear(self): + """Clear the list of recorded warnings.""" + self._list[:] = [] + + def __enter__(self): + if self._entered: + __tracebackhide__ = True + raise RuntimeError("Cannot enter %r twice" % self) + self._list = super(WarningsRecorder, self).__enter__() + warnings.simplefilter('always') + return self + + def __exit__(self, *exc_info): + if not self._entered: + __tracebackhide__ = True + raise RuntimeError("Cannot exit %r without entering first" % self) + super(WarningsRecorder, self).__exit__(*exc_info) + + +class WarningsChecker(WarningsRecorder): + def __init__(self, expected_warning=None): + super(WarningsChecker, self).__init__() + + msg = ("exceptions must be old-style classes or " + "derived from Warning, not %s") + if isinstance(expected_warning, tuple): + for exc in expected_warning: + if not inspect.isclass(exc): + raise TypeError(msg % type(exc)) + elif inspect.isclass(expected_warning): + expected_warning = (expected_warning,) + elif expected_warning is not None: + raise TypeError(msg % type(expected_warning)) + + self.expected_warning = expected_warning + + def __exit__(self, *exc_info): + super(WarningsChecker, self).__exit__(*exc_info) + + # only check if we're not currently handling an exception + if all(a is None for a in exc_info): + if self.expected_warning is not None: + if not any(issubclass(r.category, self.expected_warning) + for r in self): + __tracebackhide__ = True + from _pytest.runner import fail + fail("DID NOT WARN. No warnings of type {0} was emitted. " + "The list of emitted warnings is: {1}.".format( + self.expected_warning, + [each.message for each in self])) diff --git a/venv/lib/python3.5/site-packages/_pytest/resultlog.py b/venv/lib/python3.5/site-packages/_pytest/resultlog.py new file mode 100644 index 0000000..3e4b00c --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/resultlog.py @@ -0,0 +1,108 @@ +""" log machine-parseable test session result information in a plain +text file. +""" +from __future__ import absolute_import, division, print_function + +import py +import os + +def pytest_addoption(parser): + group = parser.getgroup("terminal reporting", "resultlog plugin options") + group.addoption('--resultlog', '--result-log', action="store", + metavar="path", default=None, + help="DEPRECATED path for machine-readable result log.") + +def pytest_configure(config): + resultlog = config.option.resultlog + # prevent opening resultlog on slave nodes (xdist) + if resultlog and not hasattr(config, 'slaveinput'): + dirname = os.path.dirname(os.path.abspath(resultlog)) + if not os.path.isdir(dirname): + os.makedirs(dirname) + logfile = open(resultlog, 'w', 1) # line buffered + config._resultlog = ResultLog(config, logfile) + config.pluginmanager.register(config._resultlog) + + from _pytest.deprecated import RESULT_LOG + config.warn('C1', RESULT_LOG) + +def pytest_unconfigure(config): + resultlog = getattr(config, '_resultlog', None) + if resultlog: + resultlog.logfile.close() + del config._resultlog + config.pluginmanager.unregister(resultlog) + +def generic_path(item): + chain = item.listchain() + gpath = [chain[0].name] + fspath = chain[0].fspath + fspart = False + for node in chain[1:]: + newfspath = node.fspath + if newfspath == fspath: + if fspart: + gpath.append(':') + fspart = False + else: + gpath.append('.') + else: + gpath.append('/') + fspart = True + name = node.name + if name[0] in '([': + gpath.pop() + gpath.append(name) + fspath = newfspath + return ''.join(gpath) + +class ResultLog(object): + def __init__(self, config, logfile): + self.config = config + self.logfile = logfile # preferably line buffered + + def write_log_entry(self, testpath, lettercode, longrepr): + print("%s %s" % (lettercode, testpath), file=self.logfile) + for line in longrepr.splitlines(): + print(" %s" % line, file=self.logfile) + + def log_outcome(self, report, lettercode, longrepr): + testpath = getattr(report, 'nodeid', None) + if testpath is None: + testpath = report.fspath + self.write_log_entry(testpath, lettercode, longrepr) + + def pytest_runtest_logreport(self, report): + if report.when != "call" and report.passed: + return + res = self.config.hook.pytest_report_teststatus(report=report) + code = res[1] + if code == 'x': + longrepr = str(report.longrepr) + elif code == 'X': + longrepr = '' + elif report.passed: + longrepr = "" + elif report.failed: + longrepr = str(report.longrepr) + elif report.skipped: + longrepr = str(report.longrepr[2]) + self.log_outcome(report, code, longrepr) + + def pytest_collectreport(self, report): + if not report.passed: + if report.failed: + code = "F" + longrepr = str(report.longrepr) + else: + assert report.skipped + code = "S" + longrepr = "%s:%d: %s" % report.longrepr + self.log_outcome(report, code, longrepr) + + def pytest_internalerror(self, excrepr): + reprcrash = getattr(excrepr, 'reprcrash', None) + path = getattr(reprcrash, "path", None) + if path is None: + path = "cwd:%s" % py.path.local() + self.write_log_entry(path, '!', str(excrepr)) diff --git a/venv/lib/python3.5/site-packages/_pytest/runner.py b/venv/lib/python3.5/site-packages/_pytest/runner.py new file mode 100644 index 0000000..fd0b549 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/runner.py @@ -0,0 +1,580 @@ +""" basic collect and runtest protocol implementations """ +from __future__ import absolute_import, division, print_function + +import bdb +import sys +from time import time + +import py +from _pytest._code.code import TerminalRepr, ExceptionInfo + + + +# +# pytest plugin hooks + +def pytest_addoption(parser): + group = parser.getgroup("terminal reporting", "reporting", after="general") + group.addoption('--durations', + action="store", type=int, default=None, metavar="N", + help="show N slowest setup/test durations (N=0 for all)."), + +def pytest_terminal_summary(terminalreporter): + durations = terminalreporter.config.option.durations + if durations is None: + return + tr = terminalreporter + dlist = [] + for replist in tr.stats.values(): + for rep in replist: + if hasattr(rep, 'duration'): + dlist.append(rep) + if not dlist: + return + dlist.sort(key=lambda x: x.duration) + dlist.reverse() + if not durations: + tr.write_sep("=", "slowest test durations") + else: + tr.write_sep("=", "slowest %s test durations" % durations) + dlist = dlist[:durations] + + for rep in dlist: + nodeid = rep.nodeid.replace("::()::", "::") + tr.write_line("%02.2fs %-8s %s" % + (rep.duration, rep.when, nodeid)) + +def pytest_sessionstart(session): + session._setupstate = SetupState() +def pytest_sessionfinish(session): + session._setupstate.teardown_all() + +class NodeInfo: + def __init__(self, location): + self.location = location + +def pytest_runtest_protocol(item, nextitem): + item.ihook.pytest_runtest_logstart( + nodeid=item.nodeid, location=item.location, + ) + runtestprotocol(item, nextitem=nextitem) + return True + +def runtestprotocol(item, log=True, nextitem=None): + hasrequest = hasattr(item, "_request") + if hasrequest and not item._request: + item._initrequest() + rep = call_and_report(item, "setup", log) + reports = [rep] + if rep.passed: + if item.config.option.setupshow: + show_test_item(item) + if not item.config.option.setuponly: + reports.append(call_and_report(item, "call", log)) + reports.append(call_and_report(item, "teardown", log, + nextitem=nextitem)) + # after all teardown hooks have been called + # want funcargs and request info to go away + if hasrequest: + item._request = False + item.funcargs = None + return reports + +def show_test_item(item): + """Show test function, parameters and the fixtures of the test item.""" + tw = item.config.get_terminal_writer() + tw.line() + tw.write(' ' * 8) + tw.write(item._nodeid) + used_fixtures = sorted(item._fixtureinfo.name2fixturedefs.keys()) + if used_fixtures: + tw.write(' (fixtures used: {0})'.format(', '.join(used_fixtures))) + +def pytest_runtest_setup(item): + item.session._setupstate.prepare(item) + +def pytest_runtest_call(item): + try: + item.runtest() + except Exception: + # Store trace info to allow postmortem debugging + type, value, tb = sys.exc_info() + tb = tb.tb_next # Skip *this* frame + sys.last_type = type + sys.last_value = value + sys.last_traceback = tb + del tb # Get rid of it in this namespace + raise + +def pytest_runtest_teardown(item, nextitem): + item.session._setupstate.teardown_exact(item, nextitem) + +def pytest_report_teststatus(report): + if report.when in ("setup", "teardown"): + if report.failed: + # category, shortletter, verbose-word + return "error", "E", "ERROR" + elif report.skipped: + return "skipped", "s", "SKIPPED" + else: + return "", "", "" + + +# +# Implementation + +def call_and_report(item, when, log=True, **kwds): + call = call_runtest_hook(item, when, **kwds) + hook = item.ihook + report = hook.pytest_runtest_makereport(item=item, call=call) + if log: + hook.pytest_runtest_logreport(report=report) + if check_interactive_exception(call, report): + hook.pytest_exception_interact(node=item, call=call, report=report) + return report + +def check_interactive_exception(call, report): + return call.excinfo and not ( + hasattr(report, "wasxfail") or + call.excinfo.errisinstance(skip.Exception) or + call.excinfo.errisinstance(bdb.BdbQuit)) + +def call_runtest_hook(item, when, **kwds): + hookname = "pytest_runtest_" + when + ihook = getattr(item.ihook, hookname) + return CallInfo(lambda: ihook(item=item, **kwds), when=when) + +class CallInfo: + """ Result/Exception info a function invocation. """ + #: None or ExceptionInfo object. + excinfo = None + def __init__(self, func, when): + #: context of invocation: one of "setup", "call", + #: "teardown", "memocollect" + self.when = when + self.start = time() + try: + self.result = func() + except KeyboardInterrupt: + self.stop = time() + raise + except: + self.excinfo = ExceptionInfo() + self.stop = time() + + def __repr__(self): + if self.excinfo: + status = "exception: %s" % str(self.excinfo.value) + else: + status = "result: %r" % (self.result,) + return "" % (self.when, status) + +def getslaveinfoline(node): + try: + return node._slaveinfocache + except AttributeError: + d = node.slaveinfo + ver = "%s.%s.%s" % d['version_info'][:3] + node._slaveinfocache = s = "[%s] %s -- Python %s %s" % ( + d['id'], d['sysplatform'], ver, d['executable']) + return s + +class BaseReport(object): + + def __init__(self, **kw): + self.__dict__.update(kw) + + def toterminal(self, out): + if hasattr(self, 'node'): + out.line(getslaveinfoline(self.node)) + + longrepr = self.longrepr + if longrepr is None: + return + + if hasattr(longrepr, 'toterminal'): + longrepr.toterminal(out) + else: + try: + out.line(longrepr) + except UnicodeEncodeError: + out.line("") + + def get_sections(self, prefix): + for name, content in self.sections: + if name.startswith(prefix): + yield prefix, content + + @property + def longreprtext(self): + """ + Read-only property that returns the full string representation + of ``longrepr``. + + .. versionadded:: 3.0 + """ + tw = py.io.TerminalWriter(stringio=True) + tw.hasmarkup = False + self.toterminal(tw) + exc = tw.stringio.getvalue() + return exc.strip() + + @property + def capstdout(self): + """Return captured text from stdout, if capturing is enabled + + .. versionadded:: 3.0 + """ + return ''.join(content for (prefix, content) in self.get_sections('Captured stdout')) + + @property + def capstderr(self): + """Return captured text from stderr, if capturing is enabled + + .. versionadded:: 3.0 + """ + return ''.join(content for (prefix, content) in self.get_sections('Captured stderr')) + + passed = property(lambda x: x.outcome == "passed") + failed = property(lambda x: x.outcome == "failed") + skipped = property(lambda x: x.outcome == "skipped") + + @property + def fspath(self): + return self.nodeid.split("::")[0] + +def pytest_runtest_makereport(item, call): + when = call.when + duration = call.stop-call.start + keywords = dict([(x,1) for x in item.keywords]) + excinfo = call.excinfo + sections = [] + if not call.excinfo: + outcome = "passed" + longrepr = None + else: + if not isinstance(excinfo, ExceptionInfo): + outcome = "failed" + longrepr = excinfo + elif excinfo.errisinstance(skip.Exception): + outcome = "skipped" + r = excinfo._getreprcrash() + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + if call.when == "call": + longrepr = item.repr_failure(excinfo) + else: # exception in setup or teardown + longrepr = item._repr_failure_py(excinfo, + style=item.config.option.tbstyle) + for rwhen, key, content in item._report_sections: + sections.append(("Captured %s %s" %(key, rwhen), content)) + return TestReport(item.nodeid, item.location, + keywords, outcome, longrepr, when, + sections, duration) + +class TestReport(BaseReport): + """ Basic test report object (also used for setup and teardown calls if + they fail). + """ + def __init__(self, nodeid, location, keywords, outcome, + longrepr, when, sections=(), duration=0, **extra): + #: normalized collection node id + self.nodeid = nodeid + + #: a (filesystempath, lineno, domaininfo) tuple indicating the + #: actual location of a test item - it might be different from the + #: collected one e.g. if a method is inherited from a different module. + self.location = location + + #: a name -> value dictionary containing all keywords and + #: markers associated with a test invocation. + self.keywords = keywords + + #: test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: one of 'setup', 'call', 'teardown' to indicate runtest phase. + self.when = when + + #: list of pairs ``(str, str)`` of extra information which needs to + #: marshallable. Used by pytest to add captured text + #: from ``stdout`` and ``stderr``, but may be used by other plugins + #: to add arbitrary information to reports. + self.sections = list(sections) + + #: time it took to run just the test + self.duration = duration + + self.__dict__.update(extra) + + def __repr__(self): + return "" % ( + self.nodeid, self.when, self.outcome) + +class TeardownErrorReport(BaseReport): + outcome = "failed" + when = "teardown" + def __init__(self, longrepr, **extra): + self.longrepr = longrepr + self.sections = [] + self.__dict__.update(extra) + +def pytest_make_collect_report(collector): + call = CallInfo( + lambda: list(collector.collect()), + 'collect') + longrepr = None + if not call.excinfo: + outcome = "passed" + else: + from _pytest import nose + skip_exceptions = (Skipped,) + nose.get_skip_exceptions() + if call.excinfo.errisinstance(skip_exceptions): + outcome = "skipped" + r = collector._repr_failure_py(call.excinfo, "line").reprcrash + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + errorinfo = collector.repr_failure(call.excinfo) + if not hasattr(errorinfo, "toterminal"): + errorinfo = CollectErrorRepr(errorinfo) + longrepr = errorinfo + rep = CollectReport(collector.nodeid, outcome, longrepr, + getattr(call, 'result', None)) + rep.call = call # see collect_one_node + return rep + + +class CollectReport(BaseReport): + def __init__(self, nodeid, outcome, longrepr, result, + sections=(), **extra): + self.nodeid = nodeid + self.outcome = outcome + self.longrepr = longrepr + self.result = result or [] + self.sections = list(sections) + self.__dict__.update(extra) + + @property + def location(self): + return (self.fspath, None, self.fspath) + + def __repr__(self): + return "" % ( + self.nodeid, len(self.result), self.outcome) + +class CollectErrorRepr(TerminalRepr): + def __init__(self, msg): + self.longrepr = msg + def toterminal(self, out): + out.line(self.longrepr, red=True) + +class SetupState(object): + """ shared state for setting up/tearing down test items or collectors. """ + def __init__(self): + self.stack = [] + self._finalizers = {} + + def addfinalizer(self, finalizer, colitem): + """ attach a finalizer to the given colitem. + if colitem is None, this will add a finalizer that + is called at the end of teardown_all(). + """ + assert colitem and not isinstance(colitem, tuple) + assert py.builtin.callable(finalizer) + #assert colitem in self.stack # some unit tests don't setup stack :/ + self._finalizers.setdefault(colitem, []).append(finalizer) + + def _pop_and_teardown(self): + colitem = self.stack.pop() + self._teardown_with_finalization(colitem) + + def _callfinalizers(self, colitem): + finalizers = self._finalizers.pop(colitem, None) + exc = None + while finalizers: + fin = finalizers.pop() + try: + fin() + except Exception: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = sys.exc_info() + if exc: + py.builtin._reraise(*exc) + + def _teardown_with_finalization(self, colitem): + self._callfinalizers(colitem) + if hasattr(colitem, "teardown"): + colitem.teardown() + for colitem in self._finalizers: + assert colitem is None or colitem in self.stack \ + or isinstance(colitem, tuple) + + def teardown_all(self): + while self.stack: + self._pop_and_teardown() + for key in list(self._finalizers): + self._teardown_with_finalization(key) + assert not self._finalizers + + def teardown_exact(self, item, nextitem): + needed_collectors = nextitem and nextitem.listchain() or [] + self._teardown_towards(needed_collectors) + + def _teardown_towards(self, needed_collectors): + while self.stack: + if self.stack == needed_collectors[:len(self.stack)]: + break + self._pop_and_teardown() + + def prepare(self, colitem): + """ setup objects along the collector chain to the test-method + and teardown previously setup objects.""" + needed_collectors = colitem.listchain() + self._teardown_towards(needed_collectors) + + # check if the last collection node has raised an error + for col in self.stack: + if hasattr(col, '_prepare_exc'): + py.builtin._reraise(*col._prepare_exc) + for col in needed_collectors[len(self.stack):]: + self.stack.append(col) + try: + col.setup() + except Exception: + col._prepare_exc = sys.exc_info() + raise + +def collect_one_node(collector): + ihook = collector.ihook + ihook.pytest_collectstart(collector=collector) + rep = ihook.pytest_make_collect_report(collector=collector) + call = rep.__dict__.pop("call", None) + if call and check_interactive_exception(call, rep): + ihook.pytest_exception_interact(node=collector, call=call, report=rep) + return rep + + +# ============================================================= +# Test OutcomeExceptions and helpers for creating them. + + +class OutcomeException(Exception): + """ OutcomeException and its subclass instances indicate and + contain info about test and collection outcomes. + """ + def __init__(self, msg=None, pytrace=True): + Exception.__init__(self, msg) + self.msg = msg + self.pytrace = pytrace + + def __repr__(self): + if self.msg: + val = self.msg + if isinstance(val, bytes): + val = py._builtin._totext(val, errors='replace') + return val + return "<%s instance>" %(self.__class__.__name__,) + __str__ = __repr__ + +class Skipped(OutcomeException): + # XXX hackish: on 3k we fake to live in the builtins + # in order to have Skipped exception printing shorter/nicer + __module__ = 'builtins' + + def __init__(self, msg=None, pytrace=True, allow_module_level=False): + OutcomeException.__init__(self, msg=msg, pytrace=pytrace) + self.allow_module_level = allow_module_level + + +class Failed(OutcomeException): + """ raised from an explicit call to pytest.fail() """ + __module__ = 'builtins' + + +class Exit(KeyboardInterrupt): + """ raised for immediate program exits (no tracebacks/summaries)""" + def __init__(self, msg="unknown reason"): + self.msg = msg + KeyboardInterrupt.__init__(self, msg) + +# exposed helper methods + +def exit(msg): + """ exit testing process as if KeyboardInterrupt was triggered. """ + __tracebackhide__ = True + raise Exit(msg) + + +exit.Exception = Exit + + +def skip(msg=""): + """ skip an executing test with the given message. Note: it's usually + better to use the pytest.mark.skipif marker to declare a test to be + skipped under certain conditions like mismatching platforms or + dependencies. See the pytest_skipping plugin for details. + """ + __tracebackhide__ = True + raise Skipped(msg=msg) + + +skip.Exception = Skipped + + +def fail(msg="", pytrace=True): + """ explicitly fail an currently-executing test with the given Message. + + :arg pytrace: if false the msg represents the full failure information + and no python traceback will be reported. + """ + __tracebackhide__ = True + raise Failed(msg=msg, pytrace=pytrace) + + +fail.Exception = Failed + + +def importorskip(modname, minversion=None): + """ return imported module if it has at least "minversion" as its + __version__ attribute. If no minversion is specified the a skip + is only triggered if the module can not be imported. + """ + import warnings + __tracebackhide__ = True + compile(modname, '', 'eval') # to catch syntaxerrors + should_skip = False + + with warnings.catch_warnings(): + # make sure to ignore ImportWarnings that might happen because + # of existing directories with the same name we're trying to + # import but without a __init__.py file + warnings.simplefilter('ignore') + try: + __import__(modname) + except ImportError: + # Do not raise chained exception here(#1485) + should_skip = True + if should_skip: + raise Skipped("could not import %r" %(modname,), allow_module_level=True) + mod = sys.modules[modname] + if minversion is None: + return mod + verattr = getattr(mod, '__version__', None) + if minversion is not None: + try: + from pkg_resources import parse_version as pv + except ImportError: + raise Skipped("we have a required version for %r but can not import " + "pkg_resources to parse version strings." % (modname,), + allow_module_level=True) + if verattr is None or pv(verattr) < pv(minversion): + raise Skipped("module %r has __version__ %r, required is: %r" %( + modname, verattr, minversion), allow_module_level=True) + return mod diff --git a/venv/lib/python3.5/site-packages/_pytest/setuponly.py b/venv/lib/python3.5/site-packages/_pytest/setuponly.py new file mode 100644 index 0000000..15e195a --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/setuponly.py @@ -0,0 +1,74 @@ +from __future__ import absolute_import, division, print_function + +import pytest +import sys + + +def pytest_addoption(parser): + group = parser.getgroup("debugconfig") + group.addoption('--setuponly', '--setup-only', action="store_true", + help="only setup fixtures, do not execute tests.") + group.addoption('--setupshow', '--setup-show', action="store_true", + help="show setup of fixtures while executing tests.") + + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef, request): + yield + config = request.config + if config.option.setupshow: + if hasattr(request, 'param'): + # Save the fixture parameter so ._show_fixture_action() can + # display it now and during the teardown (in .finish()). + if fixturedef.ids: + if callable(fixturedef.ids): + fixturedef.cached_param = fixturedef.ids(request.param) + else: + fixturedef.cached_param = fixturedef.ids[ + request.param_index] + else: + fixturedef.cached_param = request.param + _show_fixture_action(fixturedef, 'SETUP') + + +def pytest_fixture_post_finalizer(fixturedef): + if hasattr(fixturedef, "cached_result"): + config = fixturedef._fixturemanager.config + if config.option.setupshow: + _show_fixture_action(fixturedef, 'TEARDOWN') + if hasattr(fixturedef, "cached_param"): + del fixturedef.cached_param + + +def _show_fixture_action(fixturedef, msg): + config = fixturedef._fixturemanager.config + capman = config.pluginmanager.getplugin('capturemanager') + if capman: + out, err = capman.suspendcapture() + + tw = config.get_terminal_writer() + tw.line() + tw.write(' ' * 2 * fixturedef.scopenum) + tw.write('{step} {scope} {fixture}'.format( + step=msg.ljust(8), # align the output to TEARDOWN + scope=fixturedef.scope[0].upper(), + fixture=fixturedef.argname)) + + if msg == 'SETUP': + deps = sorted(arg for arg in fixturedef.argnames if arg != 'request') + if deps: + tw.write(' (fixtures used: {0})'.format(', '.join(deps))) + + if hasattr(fixturedef, 'cached_param'): + tw.write('[{0}]'.format(fixturedef.cached_param)) + + if capman: + capman.resumecapture() + sys.stdout.write(out) + sys.stderr.write(err) + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config): + if config.option.setuponly: + config.option.setupshow = True diff --git a/venv/lib/python3.5/site-packages/_pytest/setupplan.py b/venv/lib/python3.5/site-packages/_pytest/setupplan.py new file mode 100644 index 0000000..e11bd40 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/setupplan.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import, division, print_function + +import pytest + + +def pytest_addoption(parser): + group = parser.getgroup("debugconfig") + group.addoption('--setupplan', '--setup-plan', action="store_true", + help="show what fixtures and tests would be executed but " + "don't execute anything.") + + +@pytest.hookimpl(tryfirst=True) +def pytest_fixture_setup(fixturedef, request): + # Will return a dummy fixture if the setuponly option is provided. + if request.config.option.setupplan: + fixturedef.cached_result = (None, None, None) + return fixturedef.cached_result + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config): + if config.option.setupplan: + config.option.setuponly = True + config.option.setupshow = True diff --git a/venv/lib/python3.5/site-packages/_pytest/skipping.py b/venv/lib/python3.5/site-packages/_pytest/skipping.py new file mode 100644 index 0000000..5af1ca4 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/skipping.py @@ -0,0 +1,380 @@ +""" support for skip/xfail functions and markers. """ +from __future__ import absolute_import, division, print_function + +import os +import sys +import traceback + +import py +from _pytest.config import hookimpl +from _pytest.mark import MarkInfo, MarkDecorator +from _pytest.runner import fail, skip + +def pytest_addoption(parser): + group = parser.getgroup("general") + group.addoption('--runxfail', + action="store_true", dest="runxfail", default=False, + help="run tests even if they are marked xfail") + + parser.addini("xfail_strict", "default for the strict parameter of xfail " + "markers when not given explicitly (default: " + "False)", + default=False, + type="bool") + + +def pytest_configure(config): + if config.option.runxfail: + # yay a hack + import pytest + old = pytest.xfail + config._cleanup.append(lambda: setattr(pytest, "xfail", old)) + + def nop(*args, **kwargs): + pass + + nop.Exception = XFailed + setattr(pytest, "xfail", nop) + + config.addinivalue_line("markers", + "skip(reason=None): skip the given test function with an optional reason. " + "Example: skip(reason=\"no way of currently testing this\") skips the " + "test." + ) + config.addinivalue_line("markers", + "skipif(condition): skip the given test function if eval(condition) " + "results in a True value. Evaluation happens within the " + "module global context. Example: skipif('sys.platform == \"win32\"') " + "skips the test if we are on the win32 platform. see " + "http://pytest.org/latest/skipping.html" + ) + config.addinivalue_line("markers", + "xfail(condition, reason=None, run=True, raises=None, strict=False): " + "mark the test function as an expected failure if eval(condition) " + "has a True value. Optionally specify a reason for better reporting " + "and run=False if you don't even want to execute the test function. " + "If only specific exception(s) are expected, you can list them in " + "raises, and if the test fails in other ways, it will be reported as " + "a true failure. See http://pytest.org/latest/skipping.html" + ) + + +class XFailed(fail.Exception): + """ raised from an explicit call to pytest.xfail() """ + + +def xfail(reason=""): + """ xfail an executing test or setup functions with the given reason.""" + __tracebackhide__ = True + raise XFailed(reason) + + +xfail.Exception = XFailed + + +class MarkEvaluator: + def __init__(self, item, name): + self.item = item + self.name = name + + @property + def holder(self): + return self.item.keywords.get(self.name) + + def __bool__(self): + return bool(self.holder) + __nonzero__ = __bool__ + + def wasvalid(self): + return not hasattr(self, 'exc') + + def invalidraise(self, exc): + raises = self.get('raises') + if not raises: + return + return not isinstance(exc, raises) + + def istrue(self): + try: + return self._istrue() + except Exception: + self.exc = sys.exc_info() + if isinstance(self.exc[1], SyntaxError): + msg = [" " * (self.exc[1].offset + 4) + "^", ] + msg.append("SyntaxError: invalid syntax") + else: + msg = traceback.format_exception_only(*self.exc[:2]) + fail("Error evaluating %r expression\n" + " %s\n" + "%s" + % (self.name, self.expr, "\n".join(msg)), + pytrace=False) + + def _getglobals(self): + d = {'os': os, 'sys': sys, 'config': self.item.config} + if hasattr(self.item, 'obj'): + d.update(self.item.obj.__globals__) + return d + + def _istrue(self): + if hasattr(self, 'result'): + return self.result + if self.holder: + if self.holder.args or 'condition' in self.holder.kwargs: + self.result = False + # "holder" might be a MarkInfo or a MarkDecorator; only + # MarkInfo keeps track of all parameters it received in an + # _arglist attribute + marks = getattr(self.holder, '_marks', None) \ + or [self.holder.mark] + for _, args, kwargs in marks: + if 'condition' in kwargs: + args = (kwargs['condition'],) + for expr in args: + self.expr = expr + if isinstance(expr, py.builtin._basestring): + d = self._getglobals() + result = cached_eval(self.item.config, expr, d) + else: + if "reason" not in kwargs: + # XXX better be checked at collection time + msg = "you need to specify reason=STRING " \ + "when using booleans as conditions." + fail(msg) + result = bool(expr) + if result: + self.result = True + self.reason = kwargs.get('reason', None) + self.expr = expr + return self.result + else: + self.result = True + return getattr(self, 'result', False) + + def get(self, attr, default=None): + return self.holder.kwargs.get(attr, default) + + def getexplanation(self): + expl = getattr(self, 'reason', None) or self.get('reason', None) + if not expl: + if not hasattr(self, 'expr'): + return "" + else: + return "condition: " + str(self.expr) + return expl + + +@hookimpl(tryfirst=True) +def pytest_runtest_setup(item): + # Check if skip or skipif are specified as pytest marks + + skipif_info = item.keywords.get('skipif') + if isinstance(skipif_info, (MarkInfo, MarkDecorator)): + eval_skipif = MarkEvaluator(item, 'skipif') + if eval_skipif.istrue(): + item._evalskip = eval_skipif + skip(eval_skipif.getexplanation()) + + skip_info = item.keywords.get('skip') + if isinstance(skip_info, (MarkInfo, MarkDecorator)): + item._evalskip = True + if 'reason' in skip_info.kwargs: + skip(skip_info.kwargs['reason']) + elif skip_info.args: + skip(skip_info.args[0]) + else: + skip("unconditional skip") + + item._evalxfail = MarkEvaluator(item, 'xfail') + check_xfail_no_run(item) + + +@hookimpl(hookwrapper=True) +def pytest_pyfunc_call(pyfuncitem): + check_xfail_no_run(pyfuncitem) + outcome = yield + passed = outcome.excinfo is None + if passed: + check_strict_xfail(pyfuncitem) + + +def check_xfail_no_run(item): + """check xfail(run=False)""" + if not item.config.option.runxfail: + evalxfail = item._evalxfail + if evalxfail.istrue(): + if not evalxfail.get('run', True): + xfail("[NOTRUN] " + evalxfail.getexplanation()) + + +def check_strict_xfail(pyfuncitem): + """check xfail(strict=True) for the given PASSING test""" + evalxfail = pyfuncitem._evalxfail + if evalxfail.istrue(): + strict_default = pyfuncitem.config.getini('xfail_strict') + is_strict_xfail = evalxfail.get('strict', strict_default) + if is_strict_xfail: + del pyfuncitem._evalxfail + explanation = evalxfail.getexplanation() + fail('[XPASS(strict)] ' + explanation, pytrace=False) + + +@hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item, call): + outcome = yield + rep = outcome.get_result() + evalxfail = getattr(item, '_evalxfail', None) + evalskip = getattr(item, '_evalskip', None) + # unitttest special case, see setting of _unexpectedsuccess + if hasattr(item, '_unexpectedsuccess') and rep.when == "call": + from _pytest.compat import _is_unittest_unexpected_success_a_failure + if item._unexpectedsuccess: + rep.longrepr = "Unexpected success: {0}".format(item._unexpectedsuccess) + else: + rep.longrepr = "Unexpected success" + if _is_unittest_unexpected_success_a_failure(): + rep.outcome = "failed" + else: + rep.outcome = "passed" + rep.wasxfail = rep.longrepr + elif item.config.option.runxfail: + pass # don't interefere + elif call.excinfo and call.excinfo.errisinstance(xfail.Exception): + rep.wasxfail = "reason: " + call.excinfo.value.msg + rep.outcome = "skipped" + elif evalxfail and not rep.skipped and evalxfail.wasvalid() and \ + evalxfail.istrue(): + if call.excinfo: + if evalxfail.invalidraise(call.excinfo.value): + rep.outcome = "failed" + else: + rep.outcome = "skipped" + rep.wasxfail = evalxfail.getexplanation() + elif call.when == "call": + strict_default = item.config.getini('xfail_strict') + is_strict_xfail = evalxfail.get('strict', strict_default) + explanation = evalxfail.getexplanation() + if is_strict_xfail: + rep.outcome = "failed" + rep.longrepr = "[XPASS(strict)] {0}".format(explanation) + else: + rep.outcome = "passed" + rep.wasxfail = explanation + elif evalskip is not None and rep.skipped and type(rep.longrepr) is tuple: + # skipped by mark.skipif; change the location of the failure + # to point to the item definition, otherwise it will display + # the location of where the skip exception was raised within pytest + filename, line, reason = rep.longrepr + filename, line = item.location[:2] + rep.longrepr = filename, line, reason + +# called by terminalreporter progress reporting +def pytest_report_teststatus(report): + if hasattr(report, "wasxfail"): + if report.skipped: + return "xfailed", "x", "xfail" + elif report.passed: + return "xpassed", "X", ("XPASS", {'yellow': True}) + +# called by the terminalreporter instance/plugin +def pytest_terminal_summary(terminalreporter): + tr = terminalreporter + if not tr.reportchars: + #for name in "xfailed skipped failed xpassed": + # if not tr.stats.get(name, 0): + # tr.write_line("HINT: use '-r' option to see extra " + # "summary info about tests") + # break + return + + lines = [] + for char in tr.reportchars: + if char == "x": + show_xfailed(terminalreporter, lines) + elif char == "X": + show_xpassed(terminalreporter, lines) + elif char in "fF": + show_simple(terminalreporter, lines, 'failed', "FAIL %s") + elif char in "sS": + show_skipped(terminalreporter, lines) + elif char == "E": + show_simple(terminalreporter, lines, 'error', "ERROR %s") + elif char == 'p': + show_simple(terminalreporter, lines, 'passed', "PASSED %s") + + if lines: + tr._tw.sep("=", "short test summary info") + for line in lines: + tr._tw.line(line) + + +def show_simple(terminalreporter, lines, stat, format): + failed = terminalreporter.stats.get(stat) + if failed: + for rep in failed: + pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid) + lines.append(format % (pos,)) + + +def show_xfailed(terminalreporter, lines): + xfailed = terminalreporter.stats.get("xfailed") + if xfailed: + for rep in xfailed: + pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid) + reason = rep.wasxfail + lines.append("XFAIL %s" % (pos,)) + if reason: + lines.append(" " + str(reason)) + + +def show_xpassed(terminalreporter, lines): + xpassed = terminalreporter.stats.get("xpassed") + if xpassed: + for rep in xpassed: + pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid) + reason = rep.wasxfail + lines.append("XPASS %s %s" % (pos, reason)) + + +def cached_eval(config, expr, d): + if not hasattr(config, '_evalcache'): + config._evalcache = {} + try: + return config._evalcache[expr] + except KeyError: + import _pytest._code + exprcode = _pytest._code.compile(expr, mode="eval") + config._evalcache[expr] = x = eval(exprcode, d) + return x + + +def folded_skips(skipped): + d = {} + for event in skipped: + key = event.longrepr + assert len(key) == 3, (event, key) + d.setdefault(key, []).append(event) + l = [] + for key, events in d.items(): + l.append((len(events),) + key) + return l + + +def show_skipped(terminalreporter, lines): + tr = terminalreporter + skipped = tr.stats.get('skipped', []) + if skipped: + #if not tr.hasopt('skipped'): + # tr.write_line( + # "%d skipped tests, specify -rs for more info" % + # len(skipped)) + # return + fskips = folded_skips(skipped) + if fskips: + #tr.write_sep("_", "skipped test summary") + for num, fspath, lineno, reason in fskips: + if reason.startswith("Skipped: "): + reason = reason[9:] + lines.append( + "SKIP [%d] %s:%d: %s" % + (num, fspath, lineno, reason)) diff --git a/venv/lib/python3.5/site-packages/_pytest/terminal.py b/venv/lib/python3.5/site-packages/_pytest/terminal.py new file mode 100644 index 0000000..af89d0f --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/terminal.py @@ -0,0 +1,627 @@ +""" terminal reporting of the full testing process. + +This is a good source for looking at the various reporting hooks. +""" +from __future__ import absolute_import, division, print_function + +import itertools +from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \ + EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED +import pytest +import py +import sys +import time +import platform + +import _pytest._pluggy as pluggy + + +def pytest_addoption(parser): + group = parser.getgroup("terminal reporting", "reporting", after="general") + group._addoption('-v', '--verbose', action="count", + dest="verbose", default=0, help="increase verbosity."), + group._addoption('-q', '--quiet', action="count", + dest="quiet", default=0, help="decrease verbosity."), + group._addoption('-r', + action="store", dest="reportchars", default='', metavar="chars", + help="show extra test summary info as specified by chars (f)ailed, " + "(E)error, (s)skipped, (x)failed, (X)passed, " + "(p)passed, (P)passed with output, (a)all except pP. " + "Warnings are displayed at all times except when " + "--disable-warnings is set") + group._addoption('--disable-warnings', '--disable-pytest-warnings', default=False, + dest='disable_warnings', action='store_true', + help='disable warnings summary') + group._addoption('-l', '--showlocals', + action="store_true", dest="showlocals", default=False, + help="show locals in tracebacks (disabled by default).") + group._addoption('--tb', metavar="style", + action="store", dest="tbstyle", default='auto', + choices=['auto', 'long', 'short', 'no', 'line', 'native'], + help="traceback print mode (auto/long/short/line/native/no).") + group._addoption('--fulltrace', '--full-trace', + action="store_true", default=False, + help="don't cut any tracebacks (default is to cut).") + group._addoption('--color', metavar="color", + action="store", dest="color", default='auto', + choices=['yes', 'no', 'auto'], + help="color terminal output (yes/no/auto).") + +def pytest_configure(config): + config.option.verbose -= config.option.quiet + reporter = TerminalReporter(config, sys.stdout) + config.pluginmanager.register(reporter, 'terminalreporter') + if config.option.debug or config.option.traceconfig: + def mywriter(tags, args): + msg = " ".join(map(str, args)) + reporter.write_line("[traceconfig] " + msg) + config.trace.root.setprocessor("pytest:config", mywriter) + +def getreportopt(config): + reportopts = "" + reportchars = config.option.reportchars + if not config.option.disable_warnings and 'w' not in reportchars: + reportchars += 'w' + elif config.option.disable_warnings and 'w' in reportchars: + reportchars = reportchars.replace('w', '') + if reportchars: + for char in reportchars: + if char not in reportopts and char != 'a': + reportopts += char + elif char == 'a': + reportopts = 'fEsxXw' + return reportopts + +def pytest_report_teststatus(report): + if report.passed: + letter = "." + elif report.skipped: + letter = "s" + elif report.failed: + letter = "F" + if report.when != "call": + letter = "f" + return report.outcome, letter, report.outcome.upper() + + +class WarningReport: + """ + Simple structure to hold warnings information captured by ``pytest_logwarning``. + """ + def __init__(self, code, message, nodeid=None, fslocation=None): + """ + :param code: unused + :param str message: user friendly message about the warning + :param str|None nodeid: node id that generated the warning (see ``get_location``). + :param tuple|py.path.local fslocation: + file system location of the source of the warning (see ``get_location``). + """ + self.code = code + self.message = message + self.nodeid = nodeid + self.fslocation = fslocation + + def get_location(self, config): + """ + Returns the more user-friendly information about the location + of a warning, or None. + """ + if self.nodeid: + return self.nodeid + if self.fslocation: + if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2: + filename, linenum = self.fslocation[:2] + relpath = py.path.local(filename).relto(config.invocation_dir) + return '%s:%s' % (relpath, linenum) + else: + return str(self.fslocation) + return None + + +class TerminalReporter: + def __init__(self, config, file=None): + import _pytest.config + self.config = config + self.verbosity = self.config.option.verbose + self.showheader = self.verbosity >= 0 + self.showfspath = self.verbosity >= 0 + self.showlongtestinfo = self.verbosity > 0 + self._numcollected = 0 + + self.stats = {} + self.startdir = py.path.local() + if file is None: + file = sys.stdout + self._tw = self.writer = _pytest.config.create_terminal_writer(config, + file) + self.currentfspath = None + self.reportchars = getreportopt(config) + self.hasmarkup = self._tw.hasmarkup + self.isatty = file.isatty() + + def hasopt(self, char): + char = {'xfailed': 'x', 'skipped': 's'}.get(char, char) + return char in self.reportchars + + def write_fspath_result(self, nodeid, res): + fspath = self.config.rootdir.join(nodeid.split("::")[0]) + if fspath != self.currentfspath: + self.currentfspath = fspath + fspath = self.startdir.bestrelpath(fspath) + self._tw.line() + self._tw.write(fspath + " ") + self._tw.write(res) + + def write_ensure_prefix(self, prefix, extra="", **kwargs): + if self.currentfspath != prefix: + self._tw.line() + self.currentfspath = prefix + self._tw.write(prefix) + if extra: + self._tw.write(extra, **kwargs) + self.currentfspath = -2 + + def ensure_newline(self): + if self.currentfspath: + self._tw.line() + self.currentfspath = None + + def write(self, content, **markup): + self._tw.write(content, **markup) + + def write_line(self, line, **markup): + if not py.builtin._istext(line): + line = py.builtin.text(line, errors="replace") + self.ensure_newline() + self._tw.line(line, **markup) + + def rewrite(self, line, **markup): + line = str(line) + self._tw.write("\r" + line, **markup) + + def write_sep(self, sep, title=None, **markup): + self.ensure_newline() + self._tw.sep(sep, title, **markup) + + def section(self, title, sep="=", **kw): + self._tw.sep(sep, title, **kw) + + def line(self, msg, **kw): + self._tw.line(msg, **kw) + + def pytest_internalerror(self, excrepr): + for line in py.builtin.text(excrepr).split("\n"): + self.write_line("INTERNALERROR> " + line) + return 1 + + def pytest_logwarning(self, code, fslocation, message, nodeid): + warnings = self.stats.setdefault("warnings", []) + warning = WarningReport(code=code, fslocation=fslocation, + message=message, nodeid=nodeid) + warnings.append(warning) + + def pytest_plugin_registered(self, plugin): + if self.config.option.traceconfig: + msg = "PLUGIN registered: %s" % (plugin,) + # XXX this event may happen during setup/teardown time + # which unfortunately captures our output here + # which garbles our output if we use self.write_line + self.write_line(msg) + + def pytest_deselected(self, items): + self.stats.setdefault('deselected', []).extend(items) + + def pytest_runtest_logstart(self, nodeid, location): + # ensure that the path is printed before the + # 1st test of a module starts running + if self.showlongtestinfo: + line = self._locationline(nodeid, *location) + self.write_ensure_prefix(line, "") + elif self.showfspath: + fsid = nodeid.split("::")[0] + self.write_fspath_result(fsid, "") + + def pytest_runtest_logreport(self, report): + rep = report + res = self.config.hook.pytest_report_teststatus(report=rep) + cat, letter, word = res + self.stats.setdefault(cat, []).append(rep) + self._tests_ran = True + if not letter and not word: + # probably passed setup/teardown + return + if self.verbosity <= 0: + if not hasattr(rep, 'node') and self.showfspath: + self.write_fspath_result(rep.nodeid, letter) + else: + self._tw.write(letter) + else: + if isinstance(word, tuple): + word, markup = word + else: + if rep.passed: + markup = {'green':True} + elif rep.failed: + markup = {'red':True} + elif rep.skipped: + markup = {'yellow':True} + line = self._locationline(rep.nodeid, *rep.location) + if not hasattr(rep, 'node'): + self.write_ensure_prefix(line, word, **markup) + #self._tw.write(word, **markup) + else: + self.ensure_newline() + if hasattr(rep, 'node'): + self._tw.write("[%s] " % rep.node.gateway.id) + self._tw.write(word, **markup) + self._tw.write(" " + line) + self.currentfspath = -2 + + def pytest_collection(self): + if not self.isatty and self.config.option.verbose >= 1: + self.write("collecting ... ", bold=True) + + def pytest_collectreport(self, report): + if report.failed: + self.stats.setdefault("error", []).append(report) + elif report.skipped: + self.stats.setdefault("skipped", []).append(report) + items = [x for x in report.result if isinstance(x, pytest.Item)] + self._numcollected += len(items) + if self.isatty: + #self.write_fspath_result(report.nodeid, 'E') + self.report_collect() + + def report_collect(self, final=False): + if self.config.option.verbose < 0: + return + + errors = len(self.stats.get('error', [])) + skipped = len(self.stats.get('skipped', [])) + if final: + line = "collected " + else: + line = "collecting " + line += str(self._numcollected) + " item" + ('' if self._numcollected == 1 else 's') + if errors: + line += " / %d errors" % errors + if skipped: + line += " / %d skipped" % skipped + if self.isatty: + if final: + line += " \n" + self.rewrite(line, bold=True) + else: + self.write_line(line) + + def pytest_collection_modifyitems(self): + self.report_collect(True) + + @pytest.hookimpl(trylast=True) + def pytest_sessionstart(self, session): + self._sessionstarttime = time.time() + if not self.showheader: + return + self.write_sep("=", "test session starts", bold=True) + verinfo = platform.python_version() + msg = "platform %s -- Python %s" % (sys.platform, verinfo) + if hasattr(sys, 'pypy_version_info'): + verinfo = ".".join(map(str, sys.pypy_version_info[:3])) + msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3]) + msg += ", pytest-%s, py-%s, pluggy-%s" % ( + pytest.__version__, py.__version__, pluggy.__version__) + if self.verbosity > 0 or self.config.option.debug or \ + getattr(self.config.option, 'pastebin', None): + msg += " -- " + str(sys.executable) + self.write_line(msg) + lines = self.config.hook.pytest_report_header( + config=self.config, startdir=self.startdir) + lines.reverse() + for line in flatten(lines): + self.write_line(line) + + def pytest_report_header(self, config): + inifile = "" + if config.inifile: + inifile = " " + config.rootdir.bestrelpath(config.inifile) + lines = ["rootdir: %s, inifile:%s" % (config.rootdir, inifile)] + + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + + lines.append( + "plugins: %s" % ", ".join(_plugin_nameversions(plugininfo))) + return lines + + def pytest_collection_finish(self, session): + if self.config.option.collectonly: + self._printcollecteditems(session.items) + if self.stats.get('failed'): + self._tw.sep("!", "collection failures") + for rep in self.stats.get('failed'): + rep.toterminal(self._tw) + return 1 + return 0 + if not self.showheader: + return + #for i, testarg in enumerate(self.config.args): + # self.write_line("test path %d: %s" %(i+1, testarg)) + + def _printcollecteditems(self, items): + # to print out items and their parent collectors + # we take care to leave out Instances aka () + # because later versions are going to get rid of them anyway + if self.config.option.verbose < 0: + if self.config.option.verbose < -1: + counts = {} + for item in items: + name = item.nodeid.split('::', 1)[0] + counts[name] = counts.get(name, 0) + 1 + for name, count in sorted(counts.items()): + self._tw.line("%s: %d" % (name, count)) + else: + for item in items: + nodeid = item.nodeid + nodeid = nodeid.replace("::()::", "::") + self._tw.line(nodeid) + return + stack = [] + indent = "" + for item in items: + needed_collectors = item.listchain()[1:] # strip root node + while stack: + if stack == needed_collectors[:len(stack)]: + break + stack.pop() + for col in needed_collectors[len(stack):]: + stack.append(col) + #if col.name == "()": + # continue + indent = (len(stack) - 1) * " " + self._tw.line("%s%s" % (indent, col)) + + @pytest.hookimpl(hookwrapper=True) + def pytest_sessionfinish(self, exitstatus): + outcome = yield + outcome.get_result() + self._tw.line("") + summary_exit_codes = ( + EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR, + EXIT_NOTESTSCOLLECTED) + if exitstatus in summary_exit_codes: + self.config.hook.pytest_terminal_summary(terminalreporter=self, + exitstatus=exitstatus) + self.summary_errors() + self.summary_failures() + self.summary_warnings() + self.summary_passes() + if exitstatus == EXIT_INTERRUPTED: + self._report_keyboardinterrupt() + del self._keyboardinterrupt_memo + self.summary_deselected() + self.summary_stats() + + def pytest_keyboard_interrupt(self, excinfo): + self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) + + def pytest_unconfigure(self): + if hasattr(self, '_keyboardinterrupt_memo'): + self._report_keyboardinterrupt() + + def _report_keyboardinterrupt(self): + excrepr = self._keyboardinterrupt_memo + msg = excrepr.reprcrash.message + self.write_sep("!", msg) + if "KeyboardInterrupt" in msg: + if self.config.option.fulltrace: + excrepr.toterminal(self._tw) + else: + self._tw.line("to show a full traceback on KeyboardInterrupt use --fulltrace", yellow=True) + excrepr.reprcrash.toterminal(self._tw) + + def _locationline(self, nodeid, fspath, lineno, domain): + def mkrel(nodeid): + line = self.config.cwd_relative_nodeid(nodeid) + if domain and line.endswith(domain): + line = line[:-len(domain)] + l = domain.split("[") + l[0] = l[0].replace('.', '::') # don't replace '.' in params + line += "[".join(l) + return line + # collect_fspath comes from testid which has a "/"-normalized path + + if fspath: + res = mkrel(nodeid).replace("::()", "") # parens-normalization + if nodeid.split("::")[0] != fspath.replace("\\", "/"): + res += " <- " + self.startdir.bestrelpath(fspath) + else: + res = "[location]" + return res + " " + + def _getfailureheadline(self, rep): + if hasattr(rep, 'location'): + fspath, lineno, domain = rep.location + return domain + else: + return "test session" # XXX? + + def _getcrashline(self, rep): + try: + return str(rep.longrepr.reprcrash) + except AttributeError: + try: + return str(rep.longrepr)[:50] + except AttributeError: + return "" + + # + # summaries for sessionfinish + # + def getreports(self, name): + l = [] + for x in self.stats.get(name, []): + if not hasattr(x, '_pdbshown'): + l.append(x) + return l + + def summary_warnings(self): + if self.hasopt("w"): + all_warnings = self.stats.get("warnings") + if not all_warnings: + return + + grouped = itertools.groupby(all_warnings, key=lambda wr: wr.get_location(self.config)) + + self.write_sep("=", "warnings summary", yellow=True, bold=False) + for location, warnings in grouped: + self._tw.line(str(location) or '') + for w in warnings: + lines = w.message.splitlines() + indented = '\n'.join(' ' + x for x in lines) + self._tw.line(indented) + self._tw.line() + self._tw.line('-- Docs: http://doc.pytest.org/en/latest/warnings.html') + + def summary_passes(self): + if self.config.option.tbstyle != "no": + if self.hasopt("P"): + reports = self.getreports('passed') + if not reports: + return + self.write_sep("=", "PASSES") + for rep in reports: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg) + self._outrep_summary(rep) + + def print_teardown_sections(self, rep): + for secname, content in rep.sections: + if 'teardown' in secname: + self._tw.sep('-', secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + + def summary_failures(self): + if self.config.option.tbstyle != "no": + reports = self.getreports('failed') + if not reports: + return + self.write_sep("=", "FAILURES") + for rep in reports: + if self.config.option.tbstyle == "line": + line = self._getcrashline(rep) + self.write_line(line) + else: + msg = self._getfailureheadline(rep) + markup = {'red': True, 'bold': True} + self.write_sep("_", msg, **markup) + self._outrep_summary(rep) + for report in self.getreports(''): + if report.nodeid == rep.nodeid and report.when == 'teardown': + self.print_teardown_sections(report) + + def summary_errors(self): + if self.config.option.tbstyle != "no": + reports = self.getreports('error') + if not reports: + return + self.write_sep("=", "ERRORS") + for rep in self.stats['error']: + msg = self._getfailureheadline(rep) + if not hasattr(rep, 'when'): + # collect + msg = "ERROR collecting " + msg + elif rep.when == "setup": + msg = "ERROR at setup of " + msg + elif rep.when == "teardown": + msg = "ERROR at teardown of " + msg + self.write_sep("_", msg) + self._outrep_summary(rep) + + def _outrep_summary(self, rep): + rep.toterminal(self._tw) + for secname, content in rep.sections: + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_stats(self): + session_duration = time.time() - self._sessionstarttime + (line, color) = build_summary_stats_line(self.stats) + msg = "%s in %.2f seconds" % (line, session_duration) + markup = {color: True, 'bold': True} + + if self.verbosity >= 0: + self.write_sep("=", msg, **markup) + if self.verbosity == -1: + self.write_line(msg, **markup) + + def summary_deselected(self): + if 'deselected' in self.stats: + self.write_sep("=", "%d tests deselected" % ( + len(self.stats['deselected'])), bold=True) + +def repr_pythonversion(v=None): + if v is None: + v = sys.version_info + try: + return "%s.%s.%s-%s-%s" % v + except (TypeError, ValueError): + return str(v) + +def flatten(l): + for x in l: + if isinstance(x, (list, tuple)): + for y in flatten(x): + yield y + else: + yield x + +def build_summary_stats_line(stats): + keys = ("failed passed skipped deselected " + "xfailed xpassed warnings error").split() + unknown_key_seen = False + for key in stats.keys(): + if key not in keys: + if key: # setup/teardown reports have an empty key, ignore them + keys.append(key) + unknown_key_seen = True + parts = [] + for key in keys: + val = stats.get(key, None) + if val: + parts.append("%d %s" % (len(val), key)) + + if parts: + line = ", ".join(parts) + else: + line = "no tests ran" + + if 'failed' in stats or 'error' in stats: + color = 'red' + elif 'warnings' in stats or unknown_key_seen: + color = 'yellow' + elif 'passed' in stats: + color = 'green' + else: + color = 'yellow' + + return (line, color) + + +def _plugin_nameversions(plugininfo): + l = [] + for plugin, dist in plugininfo: + # gets us name and version! + name = '{dist.project_name}-{dist.version}'.format(dist=dist) + # questionable convenience, but it keeps things short + if name.startswith("pytest-"): + name = name[7:] + # we decided to print python package names + # they can have more than one plugin + if name not in l: + l.append(name) + return l diff --git a/venv/lib/python3.5/site-packages/_pytest/tmpdir.py b/venv/lib/python3.5/site-packages/_pytest/tmpdir.py new file mode 100644 index 0000000..5960140 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/tmpdir.py @@ -0,0 +1,126 @@ +""" support for providing temporary directories to test functions. """ +from __future__ import absolute_import, division, print_function + +import re + +import pytest +import py +from _pytest.monkeypatch import MonkeyPatch + + +class TempdirFactory: + """Factory for temporary directories under the common base temp directory. + + The base directory can be configured using the ``--basetemp`` option. + """ + + def __init__(self, config): + self.config = config + self.trace = config.trace.get("tmpdir") + + def ensuretemp(self, string, dir=1): + """ (deprecated) return temporary directory path with + the given string as the trailing part. It is usually + better to use the 'tmpdir' function argument which + provides an empty unique-per-test-invocation directory + and is guaranteed to be empty. + """ + #py.log._apiwarn(">1.1", "use tmpdir function argument") + return self.getbasetemp().ensure(string, dir=dir) + + def mktemp(self, basename, numbered=True): + """Create a subdirectory of the base temporary directory and return it. + If ``numbered``, ensure the directory is unique by adding a number + prefix greater than any existing one. + """ + basetemp = self.getbasetemp() + if not numbered: + p = basetemp.mkdir(basename) + else: + p = py.path.local.make_numbered_dir(prefix=basename, + keep=0, rootdir=basetemp, lock_timeout=None) + self.trace("mktemp", p) + return p + + def getbasetemp(self): + """ return base temporary directory. """ + try: + return self._basetemp + except AttributeError: + basetemp = self.config.option.basetemp + if basetemp: + basetemp = py.path.local(basetemp) + if basetemp.check(): + basetemp.remove() + basetemp.mkdir() + else: + temproot = py.path.local.get_temproot() + user = get_user() + if user: + # use a sub-directory in the temproot to speed-up + # make_numbered_dir() call + rootdir = temproot.join('pytest-of-%s' % user) + else: + rootdir = temproot + rootdir.ensure(dir=1) + basetemp = py.path.local.make_numbered_dir(prefix='pytest-', + rootdir=rootdir) + self._basetemp = t = basetemp.realpath() + self.trace("new basetemp", t) + return t + + def finish(self): + self.trace("finish") + + +def get_user(): + """Return the current user name, or None if getuser() does not work + in the current environment (see #1010). + """ + import getpass + try: + return getpass.getuser() + except (ImportError, KeyError): + return None + + +# backward compatibility +TempdirHandler = TempdirFactory + + +def pytest_configure(config): + """Create a TempdirFactory and attach it to the config object. + + This is to comply with existing plugins which expect the handler to be + available at pytest_configure time, but ideally should be moved entirely + to the tmpdir_factory session fixture. + """ + mp = MonkeyPatch() + t = TempdirFactory(config) + config._cleanup.extend([mp.undo, t.finish]) + mp.setattr(config, '_tmpdirhandler', t, raising=False) + mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False) + + +@pytest.fixture(scope='session') +def tmpdir_factory(request): + """Return a TempdirFactory instance for the test session. + """ + return request.config._tmpdirhandler + + +@pytest.fixture +def tmpdir(request, tmpdir_factory): + """Return a temporary directory path object + which is unique to each test function invocation, + created as a sub directory of the base temporary + directory. The returned object is a `py.path.local`_ + path object. + """ + name = request.node.name + name = re.sub(r"[\W]", "_", name) + MAXVAL = 30 + if len(name) > MAXVAL: + name = name[:MAXVAL] + x = tmpdir_factory.mktemp(name, numbered=True) + return x diff --git a/venv/lib/python3.5/site-packages/_pytest/unittest.py b/venv/lib/python3.5/site-packages/_pytest/unittest.py new file mode 100644 index 0000000..0cf0f17 --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/unittest.py @@ -0,0 +1,239 @@ +""" discovery and running of std-library "unittest" style tests. """ +from __future__ import absolute_import, division, print_function + +import sys +import traceback + +# for transferring markers +import _pytest._code +from _pytest.config import hookimpl +from _pytest.runner import fail, skip +from _pytest.python import transfer_markers, Class, Module, Function +from _pytest.skipping import MarkEvaluator, xfail + + +def pytest_pycollect_makeitem(collector, name, obj): + # has unittest been imported and is obj a subclass of its TestCase? + try: + if not issubclass(obj, sys.modules["unittest"].TestCase): + return + except Exception: + return + # yes, so let's collect it + return UnitTestCase(name, parent=collector) + + +class UnitTestCase(Class): + # marker for fixturemanger.getfixtureinfo() + # to declare that our children do not support funcargs + nofuncargs = True + + def setup(self): + cls = self.obj + if getattr(cls, '__unittest_skip__', False): + return # skipped + setup = getattr(cls, 'setUpClass', None) + if setup is not None: + setup() + teardown = getattr(cls, 'tearDownClass', None) + if teardown is not None: + self.addfinalizer(teardown) + super(UnitTestCase, self).setup() + + def collect(self): + from unittest import TestLoader + cls = self.obj + if not getattr(cls, "__test__", True): + return + self.session._fixturemanager.parsefactories(self, unittest=True) + loader = TestLoader() + module = self.getparent(Module).obj + foundsomething = False + for name in loader.getTestCaseNames(self.obj): + x = getattr(self.obj, name) + if not getattr(x, '__test__', True): + continue + funcobj = getattr(x, 'im_func', x) + transfer_markers(funcobj, cls, module) + yield TestCaseFunction(name, parent=self) + foundsomething = True + + if not foundsomething: + runtest = getattr(self.obj, 'runTest', None) + if runtest is not None: + ut = sys.modules.get("twisted.trial.unittest", None) + if ut is None or runtest != ut.TestCase.runTest: + yield TestCaseFunction('runTest', parent=self) + + +class TestCaseFunction(Function): + _excinfo = None + + def setup(self): + self._testcase = self.parent.obj(self.name) + self._fix_unittest_skip_decorator() + self._obj = getattr(self._testcase, self.name) + if hasattr(self._testcase, 'setup_method'): + self._testcase.setup_method(self._obj) + if hasattr(self, "_request"): + self._request._fillfixtures() + + def _fix_unittest_skip_decorator(self): + """ + The @unittest.skip decorator calls functools.wraps(self._testcase) + The call to functools.wraps() fails unless self._testcase + has a __name__ attribute. This is usually automatically supplied + if the test is a function or method, but we need to add manually + here. + + See issue #1169 + """ + if sys.version_info[0] == 2: + setattr(self._testcase, "__name__", self.name) + + def teardown(self): + if hasattr(self._testcase, 'teardown_method'): + self._testcase.teardown_method(self._obj) + # Allow garbage collection on TestCase instance attributes. + self._testcase = None + self._obj = None + + def startTest(self, testcase): + pass + + def _addexcinfo(self, rawexcinfo): + # unwrap potential exception info (see twisted trial support below) + rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo) + try: + excinfo = _pytest._code.ExceptionInfo(rawexcinfo) + except TypeError: + try: + try: + l = traceback.format_exception(*rawexcinfo) + l.insert(0, "NOTE: Incompatible Exception Representation, " + "displaying natively:\n\n") + fail("".join(l), pytrace=False) + except (fail.Exception, KeyboardInterrupt): + raise + except: + fail("ERROR: Unknown Incompatible Exception " + "representation:\n%r" % (rawexcinfo,), pytrace=False) + except KeyboardInterrupt: + raise + except fail.Exception: + excinfo = _pytest._code.ExceptionInfo() + self.__dict__.setdefault('_excinfo', []).append(excinfo) + + def addError(self, testcase, rawexcinfo): + self._addexcinfo(rawexcinfo) + + def addFailure(self, testcase, rawexcinfo): + self._addexcinfo(rawexcinfo) + + def addSkip(self, testcase, reason): + try: + skip(reason) + except skip.Exception: + self._evalskip = MarkEvaluator(self, 'SkipTest') + self._evalskip.result = True + self._addexcinfo(sys.exc_info()) + + def addExpectedFailure(self, testcase, rawexcinfo, reason=""): + try: + xfail(str(reason)) + except xfail.Exception: + self._addexcinfo(sys.exc_info()) + + def addUnexpectedSuccess(self, testcase, reason=""): + self._unexpectedsuccess = reason + + def addSuccess(self, testcase): + pass + + def stopTest(self, testcase): + pass + + def _handle_skip(self): + # implements the skipping machinery (see #2137) + # analog to pythons Lib/unittest/case.py:run + testMethod = getattr(self._testcase, self._testcase._testMethodName) + if (getattr(self._testcase.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False)): + # If the class or method was skipped. + skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') or + getattr(testMethod, '__unittest_skip_why__', '')) + try: # PY3, unittest2 on PY2 + self._testcase._addSkip(self, self._testcase, skip_why) + except TypeError: # PY2 + if sys.version_info[0] != 2: + raise + self._testcase._addSkip(self, skip_why) + return True + return False + + def runtest(self): + if self.config.pluginmanager.get_plugin("pdbinvoke") is None: + self._testcase(result=self) + else: + # disables tearDown and cleanups for post mortem debugging (see #1890) + if self._handle_skip(): + return + self._testcase.debug() + + def _prunetraceback(self, excinfo): + Function._prunetraceback(self, excinfo) + traceback = excinfo.traceback.filter( + lambda x: not x.frame.f_globals.get('__unittest')) + if traceback: + excinfo.traceback = traceback + + +@hookimpl(tryfirst=True) +def pytest_runtest_makereport(item, call): + if isinstance(item, TestCaseFunction): + if item._excinfo: + call.excinfo = item._excinfo.pop(0) + try: + del call.result + except AttributeError: + pass + +# twisted trial support + + +@hookimpl(hookwrapper=True) +def pytest_runtest_protocol(item): + if isinstance(item, TestCaseFunction) and \ + 'twisted.trial.unittest' in sys.modules: + ut = sys.modules['twisted.python.failure'] + Failure__init__ = ut.Failure.__init__ + check_testcase_implements_trial_reporter() + + def excstore(self, exc_value=None, exc_type=None, exc_tb=None, + captureVars=None): + if exc_value is None: + self._rawexcinfo = sys.exc_info() + else: + if exc_type is None: + exc_type = type(exc_value) + self._rawexcinfo = (exc_type, exc_value, exc_tb) + try: + Failure__init__(self, exc_value, exc_type, exc_tb, + captureVars=captureVars) + except TypeError: + Failure__init__(self, exc_value, exc_type, exc_tb) + + ut.Failure.__init__ = excstore + yield + ut.Failure.__init__ = Failure__init__ + else: + yield + + +def check_testcase_implements_trial_reporter(done=[]): + if done: + return + from zope.interface import classImplements + from twisted.trial.itrial import IReporter + classImplements(TestCaseFunction, IReporter) + done.append(1) diff --git a/venv/lib/python3.5/site-packages/_pytest/vendored_packages/__init__.py b/venv/lib/python3.5/site-packages/_pytest/vendored_packages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py b/venv/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py new file mode 100644 index 0000000..aebddad --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py @@ -0,0 +1,802 @@ +""" +PluginManager, basic initialization and tracing. + +pluggy is the cristallized core of plugin management as used +by some 150 plugins for pytest. + +Pluggy uses semantic versioning. Breaking changes are only foreseen for +Major releases (incremented X in "X.Y.Z"). If you want to use pluggy in +your project you should thus use a dependency restriction like +"pluggy>=0.1.0,<1.0" to avoid surprises. + +pluggy is concerned with hook specification, hook implementations and hook +calling. For any given hook specification a hook call invokes up to N implementations. +A hook implementation can influence its position and type of execution: +if attributed "tryfirst" or "trylast" it will be tried to execute +first or last. However, if attributed "hookwrapper" an implementation +can wrap all calls to non-hookwrapper implementations. A hookwrapper +can thus execute some code ahead and after the execution of other hooks. + +Hook specification is done by way of a regular python function where +both the function name and the names of all its arguments are significant. +Each hook implementation function is verified against the original specification +function, including the names of all its arguments. To allow for hook specifications +to evolve over the livetime of a project, hook implementations can +accept less arguments. One can thus add new arguments and semantics to +a hook specification by adding another argument typically without breaking +existing hook implementations. + +The chosen approach is meant to let a hook designer think carefuly about +which objects are needed by an extension writer. By contrast, subclass-based +extension mechanisms often expose a lot more state and behaviour than needed, +thus restricting future developments. + +Pluggy currently consists of functionality for: + +- a way to register new hook specifications. Without a hook + specification no hook calling can be performed. + +- a registry of plugins which contain hook implementation functions. It + is possible to register plugins for which a hook specification is not yet + known and validate all hooks when the system is in a more referentially + consistent state. Setting an "optionalhook" attribution to a hook + implementation will avoid PluginValidationError's if a specification + is missing. This allows to have optional integration between plugins. + +- a "hook" relay object from which you can launch 1:N calls to + registered hook implementation functions + +- a mechanism for ordering hook implementation functions + +- mechanisms for two different type of 1:N calls: "firstresult" for when + the call should stop when the first implementation returns a non-None result. + And the other (default) way of guaranteeing that all hook implementations + will be called and their non-None result collected. + +- mechanisms for "historic" extension points such that all newly + registered functions will receive all hook calls that happened + before their registration. + +- a mechanism for discovering plugin objects which are based on + setuptools based entry points. + +- a simple tracing mechanism, including tracing of plugin calls and + their arguments. + +""" +import sys +import inspect + +__version__ = '0.4.0' + +__all__ = ["PluginManager", "PluginValidationError", "HookCallError", + "HookspecMarker", "HookimplMarker"] + +_py3 = sys.version_info > (3, 0) + + +class HookspecMarker: + """ Decorator helper class for marking functions as hook specifications. + + You can instantiate it with a project_name to get a decorator. + Calling PluginManager.add_hookspecs later will discover all marked functions + if the PluginManager uses the same project_name. + """ + + def __init__(self, project_name): + self.project_name = project_name + + def __call__(self, function=None, firstresult=False, historic=False): + """ if passed a function, directly sets attributes on the function + which will make it discoverable to add_hookspecs(). If passed no + function, returns a decorator which can be applied to a function + later using the attributes supplied. + + If firstresult is True the 1:N hook call (N being the number of registered + hook implementation functions) will stop at I<=N when the I'th function + returns a non-None result. + + If historic is True calls to a hook will be memorized and replayed + on later registered plugins. + + """ + def setattr_hookspec_opts(func): + if historic and firstresult: + raise ValueError("cannot have a historic firstresult hook") + setattr(func, self.project_name + "_spec", + dict(firstresult=firstresult, historic=historic)) + return func + + if function is not None: + return setattr_hookspec_opts(function) + else: + return setattr_hookspec_opts + + +class HookimplMarker: + """ Decorator helper class for marking functions as hook implementations. + + You can instantiate with a project_name to get a decorator. + Calling PluginManager.register later will discover all marked functions + if the PluginManager uses the same project_name. + """ + def __init__(self, project_name): + self.project_name = project_name + + def __call__(self, function=None, hookwrapper=False, optionalhook=False, + tryfirst=False, trylast=False): + + """ if passed a function, directly sets attributes on the function + which will make it discoverable to register(). If passed no function, + returns a decorator which can be applied to a function later using + the attributes supplied. + + If optionalhook is True a missing matching hook specification will not result + in an error (by default it is an error if no matching spec is found). + + If tryfirst is True this hook implementation will run as early as possible + in the chain of N hook implementations for a specfication. + + If trylast is True this hook implementation will run as late as possible + in the chain of N hook implementations. + + If hookwrapper is True the hook implementations needs to execute exactly + one "yield". The code before the yield is run early before any non-hookwrapper + function is run. The code after the yield is run after all non-hookwrapper + function have run. The yield receives an ``_CallOutcome`` object representing + the exception or result outcome of the inner calls (including other hookwrapper + calls). + + """ + def setattr_hookimpl_opts(func): + setattr(func, self.project_name + "_impl", + dict(hookwrapper=hookwrapper, optionalhook=optionalhook, + tryfirst=tryfirst, trylast=trylast)) + return func + + if function is None: + return setattr_hookimpl_opts + else: + return setattr_hookimpl_opts(function) + + +def normalize_hookimpl_opts(opts): + opts.setdefault("tryfirst", False) + opts.setdefault("trylast", False) + opts.setdefault("hookwrapper", False) + opts.setdefault("optionalhook", False) + + +class _TagTracer: + def __init__(self): + self._tag2proc = {} + self.writer = None + self.indent = 0 + + def get(self, name): + return _TagTracerSub(self, (name,)) + + def format_message(self, tags, args): + if isinstance(args[-1], dict): + extra = args[-1] + args = args[:-1] + else: + extra = {} + + content = " ".join(map(str, args)) + indent = " " * self.indent + + lines = [ + "%s%s [%s]\n" % (indent, content, ":".join(tags)) + ] + + for name, value in extra.items(): + lines.append("%s %s: %s\n" % (indent, name, value)) + return lines + + def processmessage(self, tags, args): + if self.writer is not None and args: + lines = self.format_message(tags, args) + self.writer(''.join(lines)) + try: + self._tag2proc[tags](tags, args) + except KeyError: + pass + + def setwriter(self, writer): + self.writer = writer + + def setprocessor(self, tags, processor): + if isinstance(tags, str): + tags = tuple(tags.split(":")) + else: + assert isinstance(tags, tuple) + self._tag2proc[tags] = processor + + +class _TagTracerSub: + def __init__(self, root, tags): + self.root = root + self.tags = tags + + def __call__(self, *args): + self.root.processmessage(self.tags, args) + + def setmyprocessor(self, processor): + self.root.setprocessor(self.tags, processor) + + def get(self, name): + return self.__class__(self.root, self.tags + (name,)) + + +def _raise_wrapfail(wrap_controller, msg): + co = wrap_controller.gi_code + raise RuntimeError("wrap_controller at %r %s:%d %s" % + (co.co_name, co.co_filename, co.co_firstlineno, msg)) + + +def _wrapped_call(wrap_controller, func): + """ Wrap calling to a function with a generator which needs to yield + exactly once. The yield point will trigger calling the wrapped function + and return its _CallOutcome to the yield point. The generator then needs + to finish (raise StopIteration) in order for the wrapped call to complete. + """ + try: + next(wrap_controller) # first yield + except StopIteration: + _raise_wrapfail(wrap_controller, "did not yield") + call_outcome = _CallOutcome(func) + try: + wrap_controller.send(call_outcome) + _raise_wrapfail(wrap_controller, "has second yield") + except StopIteration: + pass + return call_outcome.get_result() + + +class _CallOutcome: + """ Outcome of a function call, either an exception or a proper result. + Calling the ``get_result`` method will return the result or reraise + the exception raised when the function was called. """ + excinfo = None + + def __init__(self, func): + try: + self.result = func() + except BaseException: + self.excinfo = sys.exc_info() + + def force_result(self, result): + self.result = result + self.excinfo = None + + def get_result(self): + if self.excinfo is None: + return self.result + else: + ex = self.excinfo + if _py3: + raise ex[1].with_traceback(ex[2]) + _reraise(*ex) # noqa + +if not _py3: + exec(""" +def _reraise(cls, val, tb): + raise cls, val, tb +""") + + +class _TracedHookExecution: + def __init__(self, pluginmanager, before, after): + self.pluginmanager = pluginmanager + self.before = before + self.after = after + self.oldcall = pluginmanager._inner_hookexec + assert not isinstance(self.oldcall, _TracedHookExecution) + self.pluginmanager._inner_hookexec = self + + def __call__(self, hook, hook_impls, kwargs): + self.before(hook.name, hook_impls, kwargs) + outcome = _CallOutcome(lambda: self.oldcall(hook, hook_impls, kwargs)) + self.after(outcome, hook.name, hook_impls, kwargs) + return outcome.get_result() + + def undo(self): + self.pluginmanager._inner_hookexec = self.oldcall + + +class PluginManager(object): + """ Core Pluginmanager class which manages registration + of plugin objects and 1:N hook calling. + + You can register new hooks by calling ``add_hookspec(module_or_class)``. + You can register plugin objects (which contain hooks) by calling + ``register(plugin)``. The Pluginmanager is initialized with a + prefix that is searched for in the names of the dict of registered + plugin objects. An optional excludefunc allows to blacklist names which + are not considered as hooks despite a matching prefix. + + For debugging purposes you can call ``enable_tracing()`` + which will subsequently send debug information to the trace helper. + """ + + def __init__(self, project_name, implprefix=None): + """ if implprefix is given implementation functions + will be recognized if their name matches the implprefix. """ + self.project_name = project_name + self._name2plugin = {} + self._plugin2hookcallers = {} + self._plugin_distinfo = [] + self.trace = _TagTracer().get("pluginmanage") + self.hook = _HookRelay(self.trace.root.get("hook")) + self._implprefix = implprefix + self._inner_hookexec = lambda hook, methods, kwargs: \ + _MultiCall(methods, kwargs, hook.spec_opts).execute() + + def _hookexec(self, hook, methods, kwargs): + # called from all hookcaller instances. + # enable_tracing will set its own wrapping function at self._inner_hookexec + return self._inner_hookexec(hook, methods, kwargs) + + def register(self, plugin, name=None): + """ Register a plugin and return its canonical name or None if the name + is blocked from registering. Raise a ValueError if the plugin is already + registered. """ + plugin_name = name or self.get_canonical_name(plugin) + + if plugin_name in self._name2plugin or plugin in self._plugin2hookcallers: + if self._name2plugin.get(plugin_name, -1) is None: + return # blocked plugin, return None to indicate no registration + raise ValueError("Plugin already registered: %s=%s\n%s" % + (plugin_name, plugin, self._name2plugin)) + + # XXX if an error happens we should make sure no state has been + # changed at point of return + self._name2plugin[plugin_name] = plugin + + # register matching hook implementations of the plugin + self._plugin2hookcallers[plugin] = hookcallers = [] + for name in dir(plugin): + hookimpl_opts = self.parse_hookimpl_opts(plugin, name) + if hookimpl_opts is not None: + normalize_hookimpl_opts(hookimpl_opts) + method = getattr(plugin, name) + hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts) + hook = getattr(self.hook, name, None) + if hook is None: + hook = _HookCaller(name, self._hookexec) + setattr(self.hook, name, hook) + elif hook.has_spec(): + self._verify_hook(hook, hookimpl) + hook._maybe_apply_history(hookimpl) + hook._add_hookimpl(hookimpl) + hookcallers.append(hook) + return plugin_name + + def parse_hookimpl_opts(self, plugin, name): + method = getattr(plugin, name) + try: + res = getattr(method, self.project_name + "_impl", None) + except Exception: + res = {} + if res is not None and not isinstance(res, dict): + # false positive + res = None + elif res is None and self._implprefix and name.startswith(self._implprefix): + res = {} + return res + + def unregister(self, plugin=None, name=None): + """ unregister a plugin object and all its contained hook implementations + from internal data structures. """ + if name is None: + assert plugin is not None, "one of name or plugin needs to be specified" + name = self.get_name(plugin) + + if plugin is None: + plugin = self.get_plugin(name) + + # if self._name2plugin[name] == None registration was blocked: ignore + if self._name2plugin.get(name): + del self._name2plugin[name] + + for hookcaller in self._plugin2hookcallers.pop(plugin, []): + hookcaller._remove_plugin(plugin) + + return plugin + + def set_blocked(self, name): + """ block registrations of the given name, unregister if already registered. """ + self.unregister(name=name) + self._name2plugin[name] = None + + def is_blocked(self, name): + """ return True if the name blogs registering plugins of that name. """ + return name in self._name2plugin and self._name2plugin[name] is None + + def add_hookspecs(self, module_or_class): + """ add new hook specifications defined in the given module_or_class. + Functions are recognized if they have been decorated accordingly. """ + names = [] + for name in dir(module_or_class): + spec_opts = self.parse_hookspec_opts(module_or_class, name) + if spec_opts is not None: + hc = getattr(self.hook, name, None) + if hc is None: + hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts) + setattr(self.hook, name, hc) + else: + # plugins registered this hook without knowing the spec + hc.set_specification(module_or_class, spec_opts) + for hookfunction in (hc._wrappers + hc._nonwrappers): + self._verify_hook(hc, hookfunction) + names.append(name) + + if not names: + raise ValueError("did not find any %r hooks in %r" % + (self.project_name, module_or_class)) + + def parse_hookspec_opts(self, module_or_class, name): + method = getattr(module_or_class, name) + return getattr(method, self.project_name + "_spec", None) + + def get_plugins(self): + """ return the set of registered plugins. """ + return set(self._plugin2hookcallers) + + def is_registered(self, plugin): + """ Return True if the plugin is already registered. """ + return plugin in self._plugin2hookcallers + + def get_canonical_name(self, plugin): + """ Return canonical name for a plugin object. Note that a plugin + may be registered under a different name which was specified + by the caller of register(plugin, name). To obtain the name + of an registered plugin use ``get_name(plugin)`` instead.""" + return getattr(plugin, "__name__", None) or str(id(plugin)) + + def get_plugin(self, name): + """ Return a plugin or None for the given name. """ + return self._name2plugin.get(name) + + def has_plugin(self, name): + """ Return True if a plugin with the given name is registered. """ + return self.get_plugin(name) is not None + + def get_name(self, plugin): + """ Return name for registered plugin or None if not registered. """ + for name, val in self._name2plugin.items(): + if plugin == val: + return name + + def _verify_hook(self, hook, hookimpl): + if hook.is_historic() and hookimpl.hookwrapper: + raise PluginValidationError( + "Plugin %r\nhook %r\nhistoric incompatible to hookwrapper" % + (hookimpl.plugin_name, hook.name)) + + for arg in hookimpl.argnames: + if arg not in hook.argnames: + raise PluginValidationError( + "Plugin %r\nhook %r\nargument %r not available\n" + "plugin definition: %s\n" + "available hookargs: %s" % + (hookimpl.plugin_name, hook.name, arg, + _formatdef(hookimpl.function), ", ".join(hook.argnames))) + + def check_pending(self): + """ Verify that all hooks which have not been verified against + a hook specification are optional, otherwise raise PluginValidationError""" + for name in self.hook.__dict__: + if name[0] != "_": + hook = getattr(self.hook, name) + if not hook.has_spec(): + for hookimpl in (hook._wrappers + hook._nonwrappers): + if not hookimpl.optionalhook: + raise PluginValidationError( + "unknown hook %r in plugin %r" % + (name, hookimpl.plugin)) + + def load_setuptools_entrypoints(self, entrypoint_name): + """ Load modules from querying the specified setuptools entrypoint name. + Return the number of loaded plugins. """ + from pkg_resources import (iter_entry_points, DistributionNotFound, + VersionConflict) + for ep in iter_entry_points(entrypoint_name): + # is the plugin registered or blocked? + if self.get_plugin(ep.name) or self.is_blocked(ep.name): + continue + try: + plugin = ep.load() + except DistributionNotFound: + continue + except VersionConflict as e: + raise PluginValidationError( + "Plugin %r could not be loaded: %s!" % (ep.name, e)) + self.register(plugin, name=ep.name) + self._plugin_distinfo.append((plugin, ep.dist)) + return len(self._plugin_distinfo) + + def list_plugin_distinfo(self): + """ return list of distinfo/plugin tuples for all setuptools registered + plugins. """ + return list(self._plugin_distinfo) + + def list_name_plugin(self): + """ return list of name/plugin pairs. """ + return list(self._name2plugin.items()) + + def get_hookcallers(self, plugin): + """ get all hook callers for the specified plugin. """ + return self._plugin2hookcallers.get(plugin) + + def add_hookcall_monitoring(self, before, after): + """ add before/after tracing functions for all hooks + and return an undo function which, when called, + will remove the added tracers. + + ``before(hook_name, hook_impls, kwargs)`` will be called ahead + of all hook calls and receive a hookcaller instance, a list + of HookImpl instances and the keyword arguments for the hook call. + + ``after(outcome, hook_name, hook_impls, kwargs)`` receives the + same arguments as ``before`` but also a :py:class:`_CallOutcome <_pytest.vendored_packages.pluggy._CallOutcome>` object + which represents the result of the overall hook call. + """ + return _TracedHookExecution(self, before, after).undo + + def enable_tracing(self): + """ enable tracing of hook calls and return an undo function. """ + hooktrace = self.hook._trace + + def before(hook_name, methods, kwargs): + hooktrace.root.indent += 1 + hooktrace(hook_name, kwargs) + + def after(outcome, hook_name, methods, kwargs): + if outcome.excinfo is None: + hooktrace("finish", hook_name, "-->", outcome.result) + hooktrace.root.indent -= 1 + + return self.add_hookcall_monitoring(before, after) + + def subset_hook_caller(self, name, remove_plugins): + """ Return a new _HookCaller instance for the named method + which manages calls to all registered plugins except the + ones from remove_plugins. """ + orig = getattr(self.hook, name) + plugins_to_remove = [plug for plug in remove_plugins if hasattr(plug, name)] + if plugins_to_remove: + hc = _HookCaller(orig.name, orig._hookexec, orig._specmodule_or_class, + orig.spec_opts) + for hookimpl in (orig._wrappers + orig._nonwrappers): + plugin = hookimpl.plugin + if plugin not in plugins_to_remove: + hc._add_hookimpl(hookimpl) + # we also keep track of this hook caller so it + # gets properly removed on plugin unregistration + self._plugin2hookcallers.setdefault(plugin, []).append(hc) + return hc + return orig + + +class _MultiCall: + """ execute a call into multiple python functions/methods. """ + + # XXX note that the __multicall__ argument is supported only + # for pytest compatibility reasons. It was never officially + # supported there and is explicitely deprecated since 2.8 + # so we can remove it soon, allowing to avoid the below recursion + # in execute() and simplify/speed up the execute loop. + + def __init__(self, hook_impls, kwargs, specopts={}): + self.hook_impls = hook_impls + self.kwargs = kwargs + self.kwargs["__multicall__"] = self + self.specopts = specopts + + def execute(self): + all_kwargs = self.kwargs + self.results = results = [] + firstresult = self.specopts.get("firstresult") + + while self.hook_impls: + hook_impl = self.hook_impls.pop() + try: + args = [all_kwargs[argname] for argname in hook_impl.argnames] + except KeyError: + for argname in hook_impl.argnames: + if argname not in all_kwargs: + raise HookCallError( + "hook call must provide argument %r" % (argname,)) + if hook_impl.hookwrapper: + return _wrapped_call(hook_impl.function(*args), self.execute) + res = hook_impl.function(*args) + if res is not None: + if firstresult: + return res + results.append(res) + + if not firstresult: + return results + + def __repr__(self): + status = "%d meths" % (len(self.hook_impls),) + if hasattr(self, "results"): + status = ("%d results, " % len(self.results)) + status + return "<_MultiCall %s, kwargs=%r>" % (status, self.kwargs) + + +def varnames(func, startindex=None): + """ return argument name tuple for a function, method, class or callable. + + In case of a class, its "__init__" method is considered. + For methods the "self" parameter is not included unless you are passing + an unbound method with Python3 (which has no supports for unbound methods) + """ + cache = getattr(func, "__dict__", {}) + try: + return cache["_varnames"] + except KeyError: + pass + if inspect.isclass(func): + try: + func = func.__init__ + except AttributeError: + return () + startindex = 1 + else: + if not inspect.isfunction(func) and not inspect.ismethod(func): + try: + func = getattr(func, '__call__', func) + except Exception: + return () + if startindex is None: + startindex = int(inspect.ismethod(func)) + + try: + rawcode = func.__code__ + except AttributeError: + return () + try: + x = rawcode.co_varnames[startindex:rawcode.co_argcount] + except AttributeError: + x = () + else: + defaults = func.__defaults__ + if defaults: + x = x[:-len(defaults)] + try: + cache["_varnames"] = x + except TypeError: + pass + return x + + +class _HookRelay: + """ hook holder object for performing 1:N hook calls where N is the number + of registered plugins. + + """ + + def __init__(self, trace): + self._trace = trace + + +class _HookCaller(object): + def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None): + self.name = name + self._wrappers = [] + self._nonwrappers = [] + self._hookexec = hook_execute + if specmodule_or_class is not None: + assert spec_opts is not None + self.set_specification(specmodule_or_class, spec_opts) + + def has_spec(self): + return hasattr(self, "_specmodule_or_class") + + def set_specification(self, specmodule_or_class, spec_opts): + assert not self.has_spec() + self._specmodule_or_class = specmodule_or_class + specfunc = getattr(specmodule_or_class, self.name) + argnames = varnames(specfunc, startindex=inspect.isclass(specmodule_or_class)) + assert "self" not in argnames # sanity check + self.argnames = ["__multicall__"] + list(argnames) + self.spec_opts = spec_opts + if spec_opts.get("historic"): + self._call_history = [] + + def is_historic(self): + return hasattr(self, "_call_history") + + def _remove_plugin(self, plugin): + def remove(wrappers): + for i, method in enumerate(wrappers): + if method.plugin == plugin: + del wrappers[i] + return True + if remove(self._wrappers) is None: + if remove(self._nonwrappers) is None: + raise ValueError("plugin %r not found" % (plugin,)) + + def _add_hookimpl(self, hookimpl): + if hookimpl.hookwrapper: + methods = self._wrappers + else: + methods = self._nonwrappers + + if hookimpl.trylast: + methods.insert(0, hookimpl) + elif hookimpl.tryfirst: + methods.append(hookimpl) + else: + # find last non-tryfirst method + i = len(methods) - 1 + while i >= 0 and methods[i].tryfirst: + i -= 1 + methods.insert(i + 1, hookimpl) + + def __repr__(self): + return "<_HookCaller %r>" % (self.name,) + + def __call__(self, **kwargs): + assert not self.is_historic() + return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) + + def call_historic(self, proc=None, kwargs=None): + self._call_history.append((kwargs or {}, proc)) + # historizing hooks don't return results + self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) + + def call_extra(self, methods, kwargs): + """ Call the hook with some additional temporarily participating + methods using the specified kwargs as call parameters. """ + old = list(self._nonwrappers), list(self._wrappers) + for method in methods: + opts = dict(hookwrapper=False, trylast=False, tryfirst=False) + hookimpl = HookImpl(None, "", method, opts) + self._add_hookimpl(hookimpl) + try: + return self(**kwargs) + finally: + self._nonwrappers, self._wrappers = old + + def _maybe_apply_history(self, method): + if self.is_historic(): + for kwargs, proc in self._call_history: + res = self._hookexec(self, [method], kwargs) + if res and proc is not None: + proc(res[0]) + + +class HookImpl: + def __init__(self, plugin, plugin_name, function, hook_impl_opts): + self.function = function + self.argnames = varnames(self.function) + self.plugin = plugin + self.opts = hook_impl_opts + self.plugin_name = plugin_name + self.__dict__.update(hook_impl_opts) + + +class PluginValidationError(Exception): + """ plugin failed validation. """ + + +class HookCallError(Exception): + """ Hook was called wrongly. """ + + +if hasattr(inspect, 'signature'): + def _formatdef(func): + return "%s%s" % ( + func.__name__, + str(inspect.signature(func)) + ) +else: + def _formatdef(func): + return "%s%s" % ( + func.__name__, + inspect.formatargspec(*inspect.getargspec(func)) + ) diff --git a/venv/lib/python3.5/site-packages/_pytest/warnings.py b/venv/lib/python3.5/site-packages/_pytest/warnings.py new file mode 100644 index 0000000..4fe28bd --- /dev/null +++ b/venv/lib/python3.5/site-packages/_pytest/warnings.py @@ -0,0 +1,88 @@ +from __future__ import absolute_import, division, print_function + +import warnings +from contextlib import contextmanager + +import pytest + +from _pytest import compat + + +def _setoption(wmod, arg): + """ + Copy of the warning._setoption function but does not escape arguments. + """ + parts = arg.split(':') + if len(parts) > 5: + raise wmod._OptionError("too many fields (max 5): %r" % (arg,)) + while len(parts) < 5: + parts.append('') + action, message, category, module, lineno = [s.strip() + for s in parts] + action = wmod._getaction(action) + category = wmod._getcategory(category) + if lineno: + try: + lineno = int(lineno) + if lineno < 0: + raise ValueError + except (ValueError, OverflowError): + raise wmod._OptionError("invalid lineno %r" % (lineno,)) + else: + lineno = 0 + wmod.filterwarnings(action, message, category, module, lineno) + + +def pytest_addoption(parser): + group = parser.getgroup("pytest-warnings") + group.addoption( + '-W', '--pythonwarnings', action='append', + help="set which warnings to report, see -W option of python itself.") + parser.addini("filterwarnings", type="linelist", + help="Each line specifies warning filter pattern which would be passed" + "to warnings.filterwarnings. Process after -W and --pythonwarnings.") + + +@contextmanager +def catch_warnings_for_item(item): + """ + catches the warnings generated during setup/call/teardown execution + of the given item and after it is done posts them as warnings to this + item. + """ + args = item.config.getoption('pythonwarnings') or [] + inifilters = item.config.getini("filterwarnings") + with warnings.catch_warnings(record=True) as log: + for arg in args: + warnings._setoption(arg) + + for arg in inifilters: + _setoption(warnings, arg) + + yield + + for warning in log: + warn_msg = warning.message + unicode_warning = False + + if compat._PY2 and any(isinstance(m, compat.UNICODE_TYPES) for m in warn_msg.args): + new_args = [compat.safe_str(m) for m in warn_msg.args] + unicode_warning = warn_msg.args != new_args + warn_msg.args = new_args + + msg = warnings.formatwarning( + warn_msg, warning.category, + warning.filename, warning.lineno, warning.line) + item.warn("unused", msg) + + if unicode_warning: + warnings.warn( + "Warning is using unicode non convertible to ascii, " + "converting to a safe representation:\n %s" % msg, + UnicodeWarning) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_protocol(item): + with catch_warnings_for_item(item): + yield diff --git a/venv/lib/python3.5/site-packages/easy_install.py b/venv/lib/python3.5/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/venv/lib/python3.5/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..39586d2 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/DESCRIPTION.rst @@ -0,0 +1,36 @@ +pip +=== + +The `PyPA recommended +`_ +tool for installing Python packages. + +* `Installation `_ +* `Documentation `_ +* `Changelog `_ +* `Github Page `_ +* `Issue Tracking `_ +* `User mailing list `_ +* `Dev mailing list `_ +* User IRC: #pypa on Freenode. +* Dev IRC: #pypa-dev on Freenode. + + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.python.org/pypi/pip + +.. image:: https://img.shields.io/travis/pypa/pip/develop.svg + :target: http://travis-ci.org/pypa/pip + +.. image:: https://readthedocs.org/projects/pip/badge/?version=stable + :target: https://pip.pypa.io/en/stable + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. + +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/METADATA b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/METADATA new file mode 100644 index 0000000..79657d0 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/METADATA @@ -0,0 +1,65 @@ +Metadata-Version: 2.0 +Name: pip +Version: 8.1.1 +Summary: The PyPA recommended tool for installing Python packages. +Home-page: https://pip.pypa.io/ +Author: The pip developers +Author-email: python-virtualenv@groups.google.com +License: MIT +Keywords: easy_install distutils setuptools egg virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Provides-Extra: testing +Requires-Dist: mock; extra == 'testing' +Requires-Dist: pretend; extra == 'testing' +Requires-Dist: pytest; extra == 'testing' +Requires-Dist: scripttest (>=1.3); extra == 'testing' +Requires-Dist: virtualenv (>=1.10); extra == 'testing' + +pip +=== + +The `PyPA recommended +`_ +tool for installing Python packages. + +* `Installation `_ +* `Documentation `_ +* `Changelog `_ +* `Github Page `_ +* `Issue Tracking `_ +* `User mailing list `_ +* `Dev mailing list `_ +* User IRC: #pypa on Freenode. +* Dev IRC: #pypa-dev on Freenode. + + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.python.org/pypi/pip + +.. image:: https://img.shields.io/travis/pypa/pip/develop.svg + :target: http://travis-ci.org/pypa/pip + +.. image:: https://readthedocs.org/projects/pip/badge/?version=stable + :target: https://pip.pypa.io/en/stable + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_. + +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/RECORD b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/RECORD new file mode 100644 index 0000000..51722f8 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/RECORD @@ -0,0 +1,117 @@ +pip/__init__.py,sha256=fFs-ytm2H4V2evGESaozmF7U0BaGIMM0drFJZ5Ifj4s,10427 +pip/__main__.py,sha256=V6Kh-IEDEFpt1cahRE6MajUF_14qJR_Qsvn4MjWZXzE,584 +pip/basecommand.py,sha256=Zlg6SE42TIjRyt1mct0LCkgNxcKKnss3xvASJyDqucE,11429 +pip/baseparser.py,sha256=Nlc7Un9gat27xtB24SnKL_3pZZOoh62gNNRdS6tDRZY,10465 +pip/cmdoptions.py,sha256=pf24iszA39rhcJ5DjFA4oD_z5vTI0NG98qUahHs3qPM,15878 +pip/download.py,sha256=oJ3sZ8I6ct9X3eoXQ9xm_Ne0e6N85G_rWaERmMCVF2k,31722 +pip/exceptions.py,sha256=GdDhHOROBj-kW2rgerLJYXsxN8ENy1BX5RUb_Vs9TXM,7980 +pip/index.py,sha256=DsxoKRxoL4oEdaqQOctou0HN1rciulGp0EBcHtnyBR4,37235 +pip/locations.py,sha256=MqUzS8YI2wDa7oFzTQw4zM4s0Hci05yubxfU_kTXXlU,5632 +pip/pep425tags.py,sha256=4PNr9hd8OsXnKYR2q2oLzfDDhF5bFBwUZA-ZQxAClSI,11318 +pip/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 +pip/wheel.py,sha256=Ux0Ry07GGBf0jGwr2tLLVMk3e2WuIJgu5PYQfSESZvk,32080 +pip/_vendor/__init__.py,sha256=9EPZ-JLxtXMt71Fp5_pKTTe1QbJZZVlN81rsRYEvlpA,4781 +pip/commands/__init__.py,sha256=naZ1iIWRutNznOVpLj8qyn1GPE0B5rhCWCrSUOZSt4M,2145 +pip/commands/completion.py,sha256=2BEUY3jowgemiIGgUP3rpk6A9My4Eu8rTPosFxlESOE,1967 +pip/commands/download.py,sha256=dMRtH0JMBhNGlJWr1qC29vOeiBzG2K0OjOAfzdxSVgA,4804 +pip/commands/freeze.py,sha256=KmQoLf-HruqBDzc-F2-ganGVn2lboNQqppfyrMsx3SU,2774 +pip/commands/hash.py,sha256=MCt4jEFyfoce0lVeNEz1x49uaTY-VDkKiBvvxrVcHkw,1597 +pip/commands/help.py,sha256=84HWkEdnGP_AEBHnn8gJP2Te0XTXRKFoXqXopbOZTNo,982 +pip/commands/install.py,sha256=DvRVVwfUy6LV-AtNcxl9kLl7XOc7G7087ZhdD4QbP60,15628 +pip/commands/list.py,sha256=u76U5TLODQ2g53sSUA4q6WhYus7usbuWuITQJsCnP3E,7412 +pip/commands/search.py,sha256=9ClAcFzkJ_7AksTkNrUed5qzsplpBtMlJByJLqiZFqw,4777 +pip/commands/show.py,sha256=dytBbI9XV-ChpV51tsuBygZJJO-QaO2Gtz5kbLkBCZE,5815 +pip/commands/uninstall.py,sha256=tz8cXz4WdpUdnt3RvpdQwH6_SNMB50egBIZWa1dwfcc,2884 +pip/commands/wheel.py,sha256=iT92Uo8qpVILl_Yk8L7AtkFVYGmY0ep5oDeyQSpwkLs,7528 +pip/compat/__init__.py,sha256=7WN0B0XMYIldfminnT679VoEJLxNQPi9MFwCIt1_llU,4669 +pip/compat/dictconfig.py,sha256=dRrelPDWrceDSzFT51RTEVY2GuM7UDyc5Igh_tn4Fvk,23096 +pip/compat/ordereddict.py,sha256=6RQCd4PyTE4tvLUoAnsygvrreOSTV4BRDbc_4gCSkTs,4110 +pip/models/__init__.py,sha256=0Rs7_RA4DxeOkWT5Cq4CQzDrSEhvYcN3TH2cazr72PE,71 +pip/models/index.py,sha256=pUfbO__v3mD9j-2n_ClwPS8pVyx4l2wIwyvWt8GMCRA,487 +pip/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/operations/freeze.py,sha256=5Pcs6Z9-TybQ_e8fcxS4aYY9MCywxbjBGUSKg7qDWgc,4323 +pip/req/__init__.py,sha256=vFwZY8_Vc1WU1zFAespg1My_r_AT3n7cN0W9eX0EFqk,276 +pip/req/req_file.py,sha256=3eaVnPMUAjikLdC5i8hZUAf8aAOby2UxmAVFf94FOXY,11928 +pip/req/req_install.py,sha256=ZzZActkzRdXP8N6THxaUd_ZTwohN6uFndbObbDiXNo4,45952 +pip/req/req_set.py,sha256=dLkSHIaA9W52NXPU-HEzFAS-FtrVsN0CzKdMvUGHaV8,32320 +pip/req/req_uninstall.py,sha256=fdH2VgCjEC8NRYDS7fRu3ZJaBBUEy-N5muwxDX5MBNM,6897 +pip/utils/__init__.py,sha256=SSixMJeh2SdjNgra_50jaC0jdmXFewLkFh_-a3tw9ks,28256 +pip/utils/appdirs.py,sha256=KTpZANfjYw5K2tZ0_jNNdP_kMxQAns79qZWelwaJo0c,7896 +pip/utils/build.py,sha256=4smLRrfSCmXmjEnVnMFh2tBEpNcSLRe6J0ejZJ-wWJE,1312 +pip/utils/deprecation.py,sha256=DR3cKqzovYu9Pif7c9bT2KmwekfW95N3BsI45_5u38I,2239 +pip/utils/encoding.py,sha256=NQxGiFS5GbeAveLZTnx92t5r0PYqvt0iRnP2u9SGG1w,971 +pip/utils/filesystem.py,sha256=ZEVBuYM3fqr2_lgOESh4Y7fPFszGD474zVm_M3Mb5Tk,899 +pip/utils/hashes.py,sha256=oMk7cd3PbJgzpSQyXq1MytMud5f6H5Oa2YY5hYuCq6I,2866 +pip/utils/logging.py,sha256=7yWu4gZw-Qclj7X80QVdpGWkdTWGKT4LiUVKcE04pro,3327 +pip/utils/outdated.py,sha256=fNwOCL5r2EftPGhgCYGMKu032HC8cV-JAr9lp0HmToM,5455 +pip/utils/setuptools_build.py,sha256=8IGop-SZ6lxUl5HMOjLRaDlORPugIH_b_b2Y67x4jQc,240 +pip/utils/ui.py,sha256=pbDkSAeumZ6jdZcOJ2yAbx8iBgeP2zfpqNnLJK1gskQ,11597 +pip/vcs/__init__.py,sha256=lnea41zMq9HqB1Qo7hxy2IjUzk5WtBvnoloCCMR6Vk4,12349 +pip/vcs/bazaar.py,sha256=tYTwc4b4off8mr0O2o8SiGejqBDJxcbDBMSMd9-ISYc,3803 +pip/vcs/git.py,sha256=u16VCiNW_a9AaYqLri2b8-f4lOZlOYwsGpHHV3uv_dQ,10218 +pip/vcs/mercurial.py,sha256=xG6rDiwHCRytJEs23SIHBXl_SwQo2jkkdD_6rVVP5h4,3472 +pip/vcs/subversion.py,sha256=mGT7sAzuVc1u-9MPoXJNyShnRzhdJpDdGNuhhzUPv6w,8687 +pip-8.1.1.dist-info/DESCRIPTION.rst,sha256=jSvW1qOjwzndvm_p_DexGCVJfwgg3rWPMJWzf6Rmsfc,1167 +pip-8.1.1.dist-info/METADATA,sha256=p_9D2tGGDX-wd8S14XVVx0K-qOjDrrwu-CmYn9Dndlc,2362 +pip-8.1.1.dist-info/RECORD,, +pip-8.1.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +pip-8.1.1.dist-info/entry_points.txt,sha256=GWc-Wb9WUKZ1EuVWNz-G0l3BeIpbNJLx0OJbZ61AAV0,68 +pip-8.1.1.dist-info/metadata.json,sha256=wAnzudgBGV69N0kQOAgeAXIjQSbkBZhZEs98ULrfRUE,1513 +pip-8.1.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +../../../bin/pip,sha256=HFK3ZGDO7d9XgI0PgAO77DsOuZ6znbPSkGv-iY8vdvM,250 +../../../bin/pip3,sha256=HFK3ZGDO7d9XgI0PgAO77DsOuZ6znbPSkGv-iY8vdvM,250 +../../../bin/pip3.5,sha256=HFK3ZGDO7d9XgI0PgAO77DsOuZ6znbPSkGv-iY8vdvM,250 +pip-8.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/utils/__pycache__/filesystem.cpython-35.pyc,, +pip/compat/__pycache__/ordereddict.cpython-35.pyc,, +pip/commands/__pycache__/hash.cpython-35.pyc,, +pip/commands/__pycache__/help.cpython-35.pyc,, +pip/__pycache__/baseparser.cpython-35.pyc,, +pip/operations/__pycache__/__init__.cpython-35.pyc,, +pip/utils/__pycache__/outdated.cpython-35.pyc,, +pip/operations/__pycache__/freeze.cpython-35.pyc,, +pip/commands/__pycache__/wheel.cpython-35.pyc,, +pip/__pycache__/locations.cpython-35.pyc,, +pip/req/__pycache__/req_set.cpython-35.pyc,, +pip/utils/__pycache__/encoding.cpython-35.pyc,, +pip/utils/__pycache__/__init__.cpython-35.pyc,, +pip/vcs/__pycache__/mercurial.cpython-35.pyc,, +pip/utils/__pycache__/hashes.cpython-35.pyc,, +pip/compat/__pycache__/__init__.cpython-35.pyc,, +pip/__pycache__/__init__.cpython-35.pyc,, +pip/__pycache__/download.cpython-35.pyc,, +pip/commands/__pycache__/uninstall.cpython-35.pyc,, +pip/req/__pycache__/__init__.cpython-35.pyc,, +pip/commands/__pycache__/completion.cpython-35.pyc,, +pip/vcs/__pycache__/bazaar.cpython-35.pyc,, +pip/req/__pycache__/req_install.cpython-35.pyc,, +pip/__pycache__/cmdoptions.cpython-35.pyc,, +pip/req/__pycache__/req_file.cpython-35.pyc,, +pip/commands/__pycache__/freeze.cpython-35.pyc,, +pip/commands/__pycache__/install.cpython-35.pyc,, +pip/vcs/__pycache__/subversion.cpython-35.pyc,, +pip/utils/__pycache__/appdirs.cpython-35.pyc,, +pip/commands/__pycache__/search.cpython-35.pyc,, +pip/utils/__pycache__/build.cpython-35.pyc,, +pip/commands/__pycache__/show.cpython-35.pyc,, +pip/models/__pycache__/index.cpython-35.pyc,, +pip/commands/__pycache__/__init__.cpython-35.pyc,, +pip/commands/__pycache__/download.cpython-35.pyc,, +pip/__pycache__/status_codes.cpython-35.pyc,, +pip/req/__pycache__/req_uninstall.cpython-35.pyc,, +pip/__pycache__/pep425tags.cpython-35.pyc,, +pip/__pycache__/index.cpython-35.pyc,, +pip/models/__pycache__/__init__.cpython-35.pyc,, +pip/utils/__pycache__/logging.cpython-35.pyc,, +pip/compat/__pycache__/dictconfig.cpython-35.pyc,, +pip/vcs/__pycache__/__init__.cpython-35.pyc,, +pip/__pycache__/basecommand.cpython-35.pyc,, +pip/_vendor/__pycache__/__init__.cpython-35.pyc,, +pip/utils/__pycache__/deprecation.cpython-35.pyc,, +pip/vcs/__pycache__/git.cpython-35.pyc,, +pip/utils/__pycache__/ui.cpython-35.pyc,, +pip/utils/__pycache__/setuptools_build.cpython-35.pyc,, +pip/__pycache__/exceptions.cpython-35.pyc,, +pip/__pycache__/__main__.cpython-35.pyc,, +pip/__pycache__/wheel.cpython-35.pyc,, +pip/commands/__pycache__/list.cpython-35.pyc,, diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/WHEEL b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/WHEEL new file mode 100644 index 0000000..8b6dd1b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..c02a8d5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip:main +pip3 = pip:main +pip3.5 = pip:main + diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/metadata.json b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/metadata.json new file mode 100644 index 0000000..91434c5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Build Tools", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: PyPy"], "extensions": {"python.commands": {"wrap_console": {"pip": "pip:main", "pip3": "pip:main", "pip3.5": "pip:main"}}, "python.details": {"contacts": [{"email": "python-virtualenv@groups.google.com", "name": "The pip developers", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://pip.pypa.io/"}}, "python.exports": {"console_scripts": {"pip": "pip:main", "pip3": "pip:main", "pip3.5": "pip:main"}}}, "extras": ["testing"], "generator": "bdist_wheel (0.29.0)", "keywords": ["easy_install", "distutils", "setuptools", "egg", "virtualenv"], "license": "MIT", "metadata_version": "2.0", "name": "pip", "run_requires": [{"extra": "testing", "requires": ["mock", "pretend", "pytest", "scripttest (>=1.3)", "virtualenv (>=1.10)"]}], "summary": "The PyPA recommended tool for installing Python packages.", "test_requires": [{"requires": ["mock", "pretend", "pytest", "scripttest (>=1.3)", "virtualenv (>=1.10)"]}], "version": "8.1.1"} \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip-8.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.5/site-packages/pip/__init__.py b/venv/lib/python3.5/site-packages/pip/__init__.py new file mode 100644 index 0000000..51e7eaf --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/__init__.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +from __future__ import absolute_import + +import locale +import logging +import os +import optparse +import warnings + +import sys +import re + +from pip.exceptions import InstallationError, CommandError, PipError +from pip.utils import get_installed_distributions, get_prog +from pip.utils import deprecation, dist_is_editable +from pip.vcs import git, mercurial, subversion, bazaar # noqa +from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.commands import get_summaries, get_similar_commands +from pip.commands import commands_dict +from pip._vendor.requests.packages.urllib3.exceptions import ( + InsecureRequestWarning, +) + + +# assignment for flake8 to be happy + +# This fixes a peculiarity when importing via __import__ - as we are +# initialising the pip module, "from pip import cmdoptions" is recursive +# and appears not to work properly in that situation. +import pip.cmdoptions +cmdoptions = pip.cmdoptions + +# The version as used in the setup.py and the docs conf.py +__version__ = "8.1.1" + + +logger = logging.getLogger(__name__) + +# Hide the InsecureRequestWArning from urllib3 +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +def autocomplete(): + """Command and option completion for the main option parser (and options) + and its subcommands (and options). + + Enable by sourcing one of the completion shell scripts (bash or zsh). + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + subcommands = [cmd for cmd, summary in get_summaries()] + options = [] + # subcommand + try: + subcommand_name = [w for w in cwords if w in subcommands][0] + except IndexError: + subcommand_name = None + + parser = create_main_parser() + # subcommand options + if subcommand_name: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for uninstall command + if subcommand_name == 'uninstall' and not current.startswith('-'): + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = commands_dict[subcommand_name]() + options += [(opt.get_opt_string(), opt.nargs) + for opt in subcommand.parser.option_list_all + if opt.help != optparse.SUPPRESS_HELP] + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1]: + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + if current.startswith('-') or current.startswith('--'): + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + opts = (o for it in opts for o in it) + + subcommands += [i.get_opt_string() for i in opts + if i.help != optparse.SUPPRESS_HELP] + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def create_main_parser(): + parser_kw = { + 'usage': '\n%prog [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + pip_pkg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + parser.version = 'pip %s from %s (python %s)' % ( + __version__, pip_pkg_dir, sys.version[:3]) + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + parser.main = True # so the help formatter knows + + # create command listing for description + command_summaries = get_summaries() + description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries] + parser.description = '\n'.join(description) + + return parser + + +def parseopts(args): + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args + + +def check_isolated(args): + isolated = False + + if "--isolated" in args: + isolated = True + + return isolated + + +def main(args=None): + if args is None: + args = sys.argv[1:] + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parseopts(args) + except PipError as exc: + sys.stderr.write("ERROR: %s" % exc) + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip.utils.encoding.auto_decode + locale.setlocale(locale.LC_ALL, '') + command = commands_dict[cmd_name](isolated=check_isolated(cmd_args)) + return command.main(cmd_args) + + +# ########################################################### +# # Writing freeze files + +class FrozenRequirement(object): + + def __init__(self, name, req, editable, comments=()): + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + _rev_re = re.compile(r'-r(\d+)$') + _date_re = re.compile(r'-(20\d\d\d\d\d\d)$') + + @classmethod + def from_dist(cls, dist, dependency_links): + location = os.path.normcase(os.path.abspath(dist.location)) + comments = [] + from pip.vcs import vcs, get_src_requirement + if dist_is_editable(dist) and vcs.get_backend_name(location): + editable = True + try: + req = get_src_requirement(dist, location) + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + req = None + if req is None: + logger.warning( + 'Could not determine repository location of %s', location + ) + comments.append( + '## !! Could not determine repository location' + ) + req = dist.as_requirement() + editable = False + else: + editable = False + req = dist.as_requirement() + specs = req.specs + assert len(specs) == 1 and specs[0][0] in ["==", "==="], \ + 'Expected 1 spec with == or ===; specs = %r; dist = %r' % \ + (specs, dist) + version = specs[0][1] + ver_match = cls._rev_re.search(version) + date_match = cls._date_re.search(version) + if ver_match or date_match: + svn_backend = vcs.get_backend('svn') + if svn_backend: + svn_location = svn_backend().get_location( + dist, + dependency_links, + ) + if not svn_location: + logger.warning( + 'Warning: cannot find svn location for %s', req) + comments.append( + '## FIXME: could not find svn URL in dependency_links ' + 'for this package:' + ) + else: + comments.append( + '# Installing as editable to satisfy requirement %s:' % + req + ) + if ver_match: + rev = ver_match.group(1) + else: + rev = '{%s}' % date_match.group(1) + editable = True + req = '%s@%s#egg=%s' % ( + svn_location, + rev, + cls.egg_name(dist) + ) + return cls(dist.project_name, req, editable, comments) + + @staticmethod + def egg_name(dist): + name = dist.egg_name() + match = re.search(r'-py\d\.\d$', name) + if match: + name = name[:match.start()] + return name + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments) + [str(req)]) + '\n' + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/venv/lib/python3.5/site-packages/pip/__main__.py b/venv/lib/python3.5/site-packages/pip/__main__.py new file mode 100644 index 0000000..5556539 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/__main__.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import + +import os +import sys + +# If we are running from a wheel, add the wheel to sys.path +# This allows the usage python pip-*.whl/pip install pip-*.whl +if __package__ == '': + # __file__ is pip-*.whl/pip/__main__.py + # first dirname call strips of '/__main__.py', second strips off '/pip' + # Resulting path is the name of the wheel itself + # Add that to sys.path so we can import pip + path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, path) + +import pip # noqa + +if __name__ == '__main__': + sys.exit(pip.main()) diff --git a/venv/lib/python3.5/site-packages/pip/_vendor/__init__.py b/venv/lib/python3.5/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..a822a5b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,110 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = True + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +WHEEL_DIR = os.path.abspath(os.path.join(sys.prefix, 'share', 'python-wheels')) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(vendored_name, globals(), locals(), level=0) + except ImportError: + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # We can just silently allow import failures to pass here. If we + # got to this point it means that ``import pip._vendor.whatever`` + # failed and so did ``import whatever``. Since we're importing this + # upfront in an attempt to alias imports, not erroring here will + # just mean we get a regular import error whenever pip *actually* + # tries to import one of these modules to use it, which actually + # gives us a better error message than we would have otherwise + # gotten. + pass + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + + # Actually alias all of our vendored dependencies. + vendored("cachecontrol") + vendored("colorama") + vendored("distlib") + vendored("html5lib") + vendored("lockfile") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pkg_resources") + vendored("progress") + vendored("retrying") + vendored("requests") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + try: + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + except ImportError: + # Debian already unbundles these from requests. + pass + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") diff --git a/venv/lib/python3.5/site-packages/pip/basecommand.py b/venv/lib/python3.5/site-packages/pip/basecommand.py new file mode 100644 index 0000000..a07043a --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/basecommand.py @@ -0,0 +1,325 @@ +"""Base Command class, and related routines""" +from __future__ import absolute_import + +import logging +import os +import sys +import optparse +import warnings + +from pip import cmdoptions +from pip.index import PackageFinder +from pip.locations import running_under_virtualenv +from pip.download import PipSession +from pip.exceptions import (BadCommand, InstallationError, UninstallationError, + CommandError, PreviousBuildDirError) + +from pip.compat import logging_dictConfig +from pip.baseparser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip.req import InstallRequirement, parse_requirements +from pip.status_codes import ( + SUCCESS, ERROR, UNKNOWN_ERROR, VIRTUALENV_NOT_FOUND, + PREVIOUS_BUILD_DIR_ERROR, +) +from pip.utils import deprecation, get_prog, normalize_path +from pip.utils.logging import IndentingFormatter +from pip.utils.outdated import pip_version_check + + +__all__ = ['Command'] + + +logger = logging.getLogger(__name__) + + +class Command(object): + name = None + usage = None + hidden = False + log_streams = ("ext://sys.stdout", "ext://sys.stderr") + + def __init__(self, isolated=False): + parser_kw = { + 'usage': self.usage, + 'prog': '%s %s' % (get_prog(), self.name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': self.name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.parser = ConfigOptionParser(**parser_kw) + + # Commands should add options to this option group + optgroup_name = '%s Options' % self.name.capitalize() + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + def _build_session(self, options, retries=None, timeout=None): + session = PipSession( + cache=( + normalize_path(os.path.join(options.cache_dir, "http")) + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + insecure_hosts=options.trusted_hosts, + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + + return session + + def parse_args(self, args): + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + options, args = self.parse_args(args) + + if options.quiet: + if options.quiet == 1: + level = "WARNING" + if options.quiet == 2: + level = "ERROR" + else: + level = "CRITICAL" + elif options.verbose: + level = "DEBUG" + else: + level = "INFO" + + logging_dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + }, + "handlers": { + "console": { + "level": level, + "class": "pip.utils.logging.ColorizedStreamHandler", + "stream": self.log_streams[0], + "filters": ["exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": "pip.utils.logging.ColorizedStreamHandler", + "stream": self.log_streams[1], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": "pip.utils.logging.BetterRotatingFileHandler", + "filename": options.log or "/dev/null", + "delay": True, + "formatter": "indent", + }, + }, + "root": { + "level": level, + "handlers": list(filter(None, [ + "console", + "console_errors", + "user_log" if options.log else None, + ])), + }, + # Disable any logging besides WARNING unless we have DEBUG level + # logging enabled. These use both pip._vendor and the bare names + # for the case where someone unbundles our libraries. + "loggers": dict( + ( + name, + { + "level": ( + "WARNING" + if level in ["INFO", "ERROR"] + else "DEBUG" + ), + }, + ) + for name in ["pip._vendor", "distlib", "requests", "urllib3"] + ), + }) + + if sys.version_info[:2] == (2, 6): + warnings.warn( + "Python 2.6 is no longer supported by the Python core team, " + "please upgrade your Python. A future version of pip will " + "drop support for Python 2.6", + deprecation.Python26DeprecationWarning + ) + + # TODO: try to get these passing down from the command? + # without resorting to os.environ to hold these. + + if options.no_input: + os.environ['PIP_NO_INPUT'] = '1' + + if options.exists_action: + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + + if options.require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical( + 'Could not find an activated virtualenv (required).' + ) + sys.exit(VIRTUALENV_NOT_FOUND) + + try: + status = self.run(options, args) + # FIXME: all commands should return an exit status + # and when it is done, isinstance is not needed anymore + if isinstance(status, int): + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('ERROR: %s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + # Check if we're using the latest version of pip available + if (not options.disable_pip_version_check and not + getattr(options, "no_index", False)): + with self._build_session( + options, + retries=0, + timeout=min(5, options.timeout)) as session: + pip_version_check(session) + + return SUCCESS + + +class RequirementCommand(Command): + + @staticmethod + def populate_requirement_set(requirement_set, args, options, finder, + session, name, wheel_cache): + """ + Marshal cmd line args into a requirement set. + """ + for filename in options.constraints: + for req in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session, wheel_cache=wheel_cache): + requirement_set.add_requirement(req) + + for req in args: + requirement_set.add_requirement( + InstallRequirement.from_line( + req, None, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + + for req in options.editables: + requirement_set.add_requirement( + InstallRequirement.from_editable( + req, + default_vcs=options.default_vcs, + isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + + found_req_in_file = False + for filename in options.requirements: + for req in parse_requirements( + filename, + finder=finder, options=options, session=session, + wheel_cache=wheel_cache): + found_req_in_file = True + requirement_set.add_requirement(req) + # If --require-hashes was a line in a requirements file, tell + # RequirementSet about it: + requirement_set.require_hashes = options.require_hashes + + if not (args or options.editables or found_req_in_file): + opts = {'name': name} + if options.find_links: + msg = ('You must give at least one requirement to ' + '%(name)s (maybe you meant "pip %(name)s ' + '%(links)s"?)' % + dict(opts, links=' '.join(options.find_links))) + else: + msg = ('You must give at least one requirement ' + 'to %(name)s (see "pip help %(name)s")' % opts) + logger.warning(msg) + + def _build_package_finder(self, options, session): + """ + Create a package finder appropriate to this requirement command. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.info('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + return PackageFinder( + find_links=options.find_links, + format_control=options.format_control, + index_urls=index_urls, + trusted_hosts=options.trusted_hosts, + allow_all_prereleases=options.pre, + process_dependency_links=options.process_dependency_links, + session=session, + ) diff --git a/venv/lib/python3.5/site-packages/pip/baseparser.py b/venv/lib/python3.5/site-packages/pip/baseparser.py new file mode 100644 index 0000000..ccbf36b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/baseparser.py @@ -0,0 +1,292 @@ +"""Base option parser setup""" +from __future__ import absolute_import + +import sys +import optparse +import os +import re +import textwrap +from distutils.util import strtobool + +from pip._vendor.six import string_types +from pip._vendor.six.moves import configparser +from pip.locations import ( + legacy_config_file, config_basename, running_under_virtualenv, + site_config_files +) +from pip.utils import appdirs, get_terminal_size + + +_environ_prefix_re = re.compile(r"^PIP_", re.I) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): + return self._format_option_strings(option, ' <%s>', ', ') + + def _format_option_strings(self, option, mvarfmt=' <%s>', optsep=', '): + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string - evaluated as mvarfmt % metavar + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt % metavar.lower()) + + return ''.join(opts) + + def format_heading(self, heading): + if heading == 'Options': + return '' + return heading + ':\n' + + def format_usage(self, usage): + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = '\nUsage: %s\n' % self.indent_lines(textwrap.dedent(usage), " ") + return msg + + def format_description(self, description): + # leave full control over description to us + if description: + if hasattr(self.parser, 'main'): + label = 'Commands' + else: + label = 'Description' + # some doc strings have initial newlines, some don't + description = description.lstrip('\n') + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = '%s:\n%s\n' % (label, description) + return description + else: + return '' + + def format_epilog(self, epilog): + # leave full control over epilog to us + if epilog: + return epilog + else: + return '' + + def indent_lines(self, text, indent): + new_lines = [indent + line for line in text.split('\n')] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + """ + + def expand_default(self, option): + if self.parser is not None: + self.parser._update_defaults(self.parser.defaults) + return optparse.IndentedHelpFormatter.expand_default(self, option) + + +class CustomOptionParser(optparse.OptionParser): + def insert_option_group(self, idx, *args, **kwargs): + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self): + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + isolated = False + + def __init__(self, *args, **kwargs): + self.config = configparser.RawConfigParser() + self.name = kwargs.pop('name') + self.isolated = kwargs.pop("isolated", False) + self.files = self.get_config_files() + if self.files: + self.config.read(self.files) + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def get_config_files(self): + # the files returned by this method will be parsed in order with the + # first files listed being overridden by later files in standard + # ConfigParser fashion + config_file = os.environ.get('PIP_CONFIG_FILE', False) + if config_file == os.devnull: + return [] + + # at the base we have any site-wide configuration + files = list(site_config_files) + + # per-user configuration next + if not self.isolated: + if config_file and os.path.exists(config_file): + files.append(config_file) + else: + # This is the legacy config file, we consider it to be a lower + # priority than the new file location. + files.append(legacy_config_file) + + # This is the new config file, we consider it to be a higher + # priority than the legacy file. + files.append( + os.path.join( + appdirs.user_config_dir("pip"), + config_basename, + ) + ) + + # finally virtualenv configuration first trumping others + if running_under_virtualenv(): + venv_config_file = os.path.join( + sys.prefix, + config_basename, + ) + if os.path.exists(venv_config_file): + files.append(venv_config_file) + + return files + + def check_default(self, option, key, val): + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: %s" % exc) + sys.exit(3) + + def _update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + # Then go and look for the other sources of configuration: + config = {} + # 1. config files + for section in ('global', self.name): + config.update( + self.normalize_keys(self.get_config_section(section)) + ) + # 2. environmental variables + if not self.isolated: + config.update(self.normalize_keys(self.get_environ_vars())) + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in config.items(): + # ignore empty values + if not val: + continue + + option = self.get_option(key) + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false', 'count'): + val = strtobool(val) + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def normalize_keys(self, items): + """Return a config dictionary with normalized keys regardless of + whether the keys were specified in environment variables or in config + files""" + normalized = {} + for key, val in items: + key = key.replace('_', '-') + if not key.startswith('--'): + key = '--%s' % key # only prefer long opts + normalized[key] = val + return normalized + + def get_config_section(self, name): + """Get a section of a configuration""" + if self.config.has_section(name): + return self.config.items(name) + return [] + + def get_environ_vars(self): + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + if _environ_prefix_re.search(key): + yield (_environ_prefix_re.sub("", key).lower(), val) + + def get_default_values(self): + """Overridding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, string_types): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg): + self.print_usage(sys.stderr) + self.exit(2, "%s\n" % msg) diff --git a/venv/lib/python3.5/site-packages/pip/cmdoptions.py b/venv/lib/python3.5/site-packages/pip/cmdoptions.py new file mode 100644 index 0000000..1fade87 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/cmdoptions.py @@ -0,0 +1,618 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. + +""" +from __future__ import absolute_import + +from functools import partial +from optparse import OptionGroup, SUPPRESS_HELP, Option +import warnings + +from pip.index import ( + FormatControl, fmt_ctl_handle_mutual_exclude, fmt_ctl_no_binary, + fmt_ctl_no_use_wheel) +from pip.models import PyPI +from pip.locations import USER_CACHE_DIR, src_prefix +from pip.utils.hashes import STRONG_HASHES + + +def make_option_group(group, parser): + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group['name']) + for option in group['options']: + option_group.add_option(option()) + return option_group + + +def resolve_wheel_no_use_binary(options): + if not options.use_wheel: + control = options.format_control + fmt_ctl_no_use_wheel(control) + + +def check_install_build_global(options, check_options=None): + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + fmt_ctl_no_binary(control) + warnings.warn( + 'Disabling all use of wheels due to the use of --build-options ' + '/ --global-options / --install-options.', stacklevel=2) + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.') + +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) + +require_virtualenv = partial( + Option, + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=SUPPRESS_HELP) + +verbose = partial( + Option, + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output. Option is additive, and can be used up to 3 times.' +) + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.') + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help='Give less output.') + +log = partial( + Option, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + help="Path to a verbose appending log." +) + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help=SUPPRESS_HELP) + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port.") + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).") + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).') + +default_vcs = partial( + Option, + # The default version control system for editables, e.g. 'svn' + '--default-vcs', + dest='default_vcs', + type='str', + default='', + help=SUPPRESS_HELP) + +skip_requirements_regex = partial( + Option, + # A regex to be used to skip requirements + '--skip-requirements-regex', + dest='skip_requirements_regex', + type='str', + default='', + help=SUPPRESS_HELP) + + +def exists_action(): + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup.") + + +cert = partial( + Option, + '--cert', + dest='cert', + type='str', + metavar='path', + help="Path to alternate CA bundle.") + +client_cert = partial( + Option, + '--client-cert', + dest='client_cert', + type='str', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.") + +index_url = partial( + Option, + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default=PyPI.simple_url, + help='Base URL of Python Package Index (default %default).') + + +def extra_index_url(): + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help='Extra URLs of package indexes to use in addition to --index-url.' + ) + + +no_index = partial( + Option, + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead).') + + +def find_links(): + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a url or path to an html file, then parse for links to " + "archives. If a local path or file:// url that's a directory, " + "then look for archives in the directory listing.") + + +def allow_external(): + return Option( + "--allow-external", + dest="allow_external", + action="append", + default=[], + metavar="PACKAGE", + help=SUPPRESS_HELP, + ) + + +allow_all_external = partial( + Option, + "--allow-all-external", + dest="allow_all_external", + action="store_true", + default=False, + help=SUPPRESS_HELP, +) + + +def trusted_host(): + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host as trusted, even though it does not have valid " + "or any HTTPS.", + ) + + +# Remove after 7.0 +no_allow_external = partial( + Option, + "--no-allow-external", + dest="allow_all_external", + action="store_false", + default=False, + help=SUPPRESS_HELP, +) + + +# Remove --allow-insecure after 7.0 +def allow_unsafe(): + return Option( + "--allow-unverified", "--allow-insecure", + dest="allow_unverified", + action="append", + default=[], + metavar="PACKAGE", + help=SUPPRESS_HELP, + ) + +# Remove after 7.0 +no_allow_unsafe = partial( + Option, + "--no-allow-insecure", + dest="allow_all_insecure", + action="store_false", + default=False, + help=SUPPRESS_HELP +) + +# Remove after 1.5 +process_dependency_links = partial( + Option, + "--process-dependency-links", + dest="process_dependency_links", + action="store_true", + default=False, + help="Enable the processing of dependency links.", +) + + +def constraints(): + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.') + + +def requirements(): + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.') + + +def editable(): + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + +src = partial( + Option, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + metavar='dir', + default=src_prefix, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "/src". ' + 'The default for global installs is "/src".' +) + +# XXX: deprecated, remove in 9.0 +use_wheel = partial( + Option, + '--use-wheel', + dest='use_wheel', + action='store_true', + default=True, + help=SUPPRESS_HELP, +) + +# XXX: deprecated, remove in 9.0 +no_use_wheel = partial( + Option, + '--no-use-wheel', + dest='use_wheel', + action='store_false', + default=True, + help=('Do not Find and prefer wheel archives when searching indexes and ' + 'find-links locations. DEPRECATED in favour of --no-binary.'), +) + + +def _get_format_control(values, option): + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + existing = getattr(parser.values, option.dest) + fmt_ctl_handle_mutual_exclude( + value, existing.no_binary, existing.only_binary) + + +def _handle_only_binary(option, opt_str, value, parser): + existing = getattr(parser.values, option.dest) + fmt_ctl_handle_mutual_exclude( + value, existing.only_binary, existing.no_binary) + + +def no_binary(): + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=FormatControl(set(), set()), + help="Do not use binary packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all binary packages, :none: to empty the set, or one or " + "more package names with commas between them. Note that some " + "packages are tricky to compile and may fail to install when " + "this option is used on them.") + + +def only_binary(): + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=FormatControl(set(), set()), + help="Do not use source packages. Can be supplied multiple times, and " + "each time adds to the existing value. Accepts either :all: to " + "disable all source packages, :none: to empty the set, or one or " + "more package names with commas between them. Packages without " + "binary distributions will fail to install when this option is " + "used on them.") + + +cache_dir = partial( + Option, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + help="Store the cache data in ." +) + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="store_false", + help="Disable the cache.", +) + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.") + +build_dir = partial( + Option, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + metavar='dir', + help='Directory to unpack packages into and build in.' +) + +install_options = partial( + Option, + '--install-option', + dest='install_options', + action='append', + metavar='options', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.") + +global_options = partial( + Option, + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the install command.") + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories.") + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.") + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=False, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.") + +# Deprecated, Remove later +always_unzip = partial( + Option, + '-Z', '--always-unzip', + dest='always_unzip', + action='store_true', + help=SUPPRESS_HELP, +) + + +def _merge_hash(option, opt_str, value, parser): + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to %s must be a hash name ' + 'followed by a value, like --hash=sha256:abcde...' % + opt_str) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for %s are %s.' % + (opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...') + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.') + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + default_vcs, + skip_requirements_regex, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + ] +} + +non_deprecated_index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + process_dependency_links, + ] +} + +index_group = { + 'name': 'Package Index Options (including deprecated options)', + 'options': non_deprecated_index_group['options'] + [ + allow_external, + allow_all_external, + no_allow_external, + allow_unsafe, + no_allow_unsafe, + ] +} diff --git a/venv/lib/python3.5/site-packages/pip/commands/__init__.py b/venv/lib/python3.5/site-packages/pip/commands/__init__.py new file mode 100644 index 0000000..92b7ff5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/__init__.py @@ -0,0 +1,83 @@ +""" +Package containing all pip commands +""" +from __future__ import absolute_import + +from pip.commands.completion import CompletionCommand +from pip.commands.download import DownloadCommand +from pip.commands.freeze import FreezeCommand +from pip.commands.hash import HashCommand +from pip.commands.help import HelpCommand +from pip.commands.list import ListCommand +from pip.commands.search import SearchCommand +from pip.commands.show import ShowCommand +from pip.commands.install import InstallCommand +from pip.commands.uninstall import UninstallCommand +from pip.commands.wheel import WheelCommand + + +commands_dict = { + CompletionCommand.name: CompletionCommand, + FreezeCommand.name: FreezeCommand, + HashCommand.name: HashCommand, + HelpCommand.name: HelpCommand, + SearchCommand.name: SearchCommand, + ShowCommand.name: ShowCommand, + InstallCommand.name: InstallCommand, + UninstallCommand.name: UninstallCommand, + DownloadCommand.name: DownloadCommand, + ListCommand.name: ListCommand, + WheelCommand.name: WheelCommand, +} + + +commands_order = [ + InstallCommand, + DownloadCommand, + UninstallCommand, + FreezeCommand, + ListCommand, + ShowCommand, + SearchCommand, + WheelCommand, + HashCommand, + CompletionCommand, + HelpCommand, +] + + +def get_summaries(ordered=True): + """Yields sorted (command name, command summary) tuples.""" + + if ordered: + cmditems = _sort_commands(commands_dict, commands_order) + else: + cmditems = commands_dict.items() + + for name, command_class in cmditems: + yield (name, command_class.summary) + + +def get_similar_commands(name): + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return False + + +def _sort_commands(cmddict, order): + def keyfn(key): + try: + return order.index(key[1]) + except ValueError: + # unordered items should come last + return 0xff + + return sorted(cmddict.items(), key=keyfn) diff --git a/venv/lib/python3.5/site-packages/pip/commands/completion.py b/venv/lib/python3.5/site-packages/pip/commands/completion.py new file mode 100644 index 0000000..dc80af3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/completion.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import + +import sys +from pip.basecommand import Command + +BASE_COMPLETION = """ +# pip %(shell)s completion start%(script)s# pip %(shell)s completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ +_pip_completion() +{ + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 ) ) +} +complete -o default -F _pip_completion pip +""", 'zsh': """ +function _pip_completion { + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] ) ) +} +compctl -K _pip_completion pip +"""} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + name = 'completion' + summary = 'A helper command used for command completion' + + def __init__(self, *args, **kw): + super(CompletionCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + cmd_opts.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--' + shell for shell in sorted(shells)] + if options.shell in shells: + script = COMPLETION_SCRIPTS.get(options.shell, '') + print(BASE_COMPLETION % {'script': script, 'shell': options.shell}) + else: + sys.stderr.write( + 'ERROR: You must pass %s\n' % ' or '.join(shell_options) + ) diff --git a/venv/lib/python3.5/site-packages/pip/commands/download.py b/venv/lib/python3.5/site-packages/pip/commands/download.py new file mode 100644 index 0000000..4155e05 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/download.py @@ -0,0 +1,136 @@ +from __future__ import absolute_import + +import logging +import os + +from pip.req import RequirementSet +from pip.basecommand import RequirementCommand +from pip import cmdoptions +from pip.utils import ensure_dir, normalize_path +from pip.utils.build import BuildDirectory +from pip.utils.filesystem import check_path_owner + + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + name = 'download' + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + summary = 'Download packages.' + + def __init__(self, *args, **kw): + super(DownloadCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.global_options()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into ."), + ) + + index_opts = cmdoptions.make_option_group( + cmdoptions.non_deprecated_index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + options.ignore_installed = True + options.src_dir = os.path.abspath(options.src_dir) + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + with self._build_session(options) as session: + + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + ignore_installed=True, + ignore_dependencies=options.ignore_dependencies, + session=session, + isolated=options.isolated_mode, + require_hashes=options.require_hashes + ) + self.populate_requirement_set( + requirement_set, + args, + options, + finder, + session, + self.name, + None + ) + + if not requirement_set.has_requirements: + return + + requirement_set.prepare_files(finder) + + downloaded = ' '.join([ + req.name for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info( + 'Successfully downloaded %s', downloaded + ) + + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + return requirement_set diff --git a/venv/lib/python3.5/site-packages/pip/commands/freeze.py b/venv/lib/python3.5/site-packages/pip/commands/freeze.py new file mode 100644 index 0000000..0485d5f --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/freeze.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import + +import sys + +import pip +from pip.compat import stdlib_pkgs +from pip.basecommand import Command +from pip.operations.freeze import freeze +from pip.wheel import WheelCache + + +DEV_PKGS = ('pip', 'setuptools', 'distribute', 'wheel') + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + name = 'freeze' + usage = """ + %prog [options]""" + summary = 'Output installed packages in requirements format.' + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def __init__(self, *args, **kw): + super(FreezeCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirement', + action='store', + default=None, + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output.") + self.cmd_opts.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the ' + 'output.') + self.cmd_opts.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' %s' % ', '.join(DEV_PKGS)) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + format_control = pip.index.FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) + + freeze_kwargs = dict( + requirement=options.requirement, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + skip_regex=options.skip_requirements_regex, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip) + + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') diff --git a/venv/lib/python3.5/site-packages/pip/commands/hash.py b/venv/lib/python3.5/site-packages/pip/commands/hash.py new file mode 100644 index 0000000..27cca0b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/hash.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip.basecommand import Command +from pip.status_codes import ERROR +from pip.utils import read_chunks +from pip.utils.hashes import FAVORITE_HASH, STRONG_HASHES + + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + + """ + name = 'hash' + usage = '%prog [options] ...' + summary = 'Compute hashes of package archives.' + + def __init__(self, *args, **kw): + super(HashCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of %s' % + ', '.join(STRONG_HASHES)) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + logger.info('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + + +def _hash_of_file(path, algorithm): + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/venv/lib/python3.5/site-packages/pip/commands/help.py b/venv/lib/python3.5/site-packages/pip/commands/help.py new file mode 100644 index 0000000..11722f1 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/help.py @@ -0,0 +1,35 @@ +from __future__ import absolute_import + +from pip.basecommand import Command, SUCCESS +from pip.exceptions import CommandError + + +class HelpCommand(Command): + """Show help for commands""" + name = 'help' + usage = """ + %prog """ + summary = 'Show help for commands.' + + def run(self, options, args): + from pip.commands import commands_dict, get_similar_commands + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "%s"' % cmd_name] + if guess: + msg.append('maybe you meant "%s"' % guess) + + raise CommandError(' - '.join(msg)) + + command = commands_dict[cmd_name]() + command.parser.print_help() + + return SUCCESS diff --git a/venv/lib/python3.5/site-packages/pip/commands/install.py b/venv/lib/python3.5/site-packages/pip/commands/install.py new file mode 100644 index 0000000..13b328f --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/install.py @@ -0,0 +1,404 @@ +from __future__ import absolute_import + +import logging +import operator +import os +import tempfile +import shutil +import warnings +try: + import wheel +except ImportError: + wheel = None + +from pip.req import RequirementSet +from pip.basecommand import RequirementCommand +from pip.locations import virtualenv_no_global, distutils_scheme +from pip.exceptions import ( + InstallationError, CommandError, PreviousBuildDirError, +) +from pip import cmdoptions +from pip.utils import ensure_dir +from pip.utils.build import BuildDirectory +from pip.utils.deprecation import RemovedInPip10Warning +from pip.utils.filesystem import check_path_owner +from pip.wheel import WheelCache, WheelBuilder + +from pip.locations import running_under_virtualenv + +logger = logging.getLogger(__name__) + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + name = 'install' + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + summary = 'Install packages.' + + def __init__(self, *args, **kw): + super(InstallCommand, self).__init__(*args, **kw) + + default_user = True + if running_under_virtualenv(): + default_user = False + if os.geteuid() == 0: + default_user = False + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.build_dir()) + + cmd_opts.add_option( + '-t', '--target', + dest='target_dir', + metavar='dir', + default=None, + help='Install packages into . ' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' + ) + + cmd_opts.add_option( + '-d', '--download', '--download-dir', '--download-directory', + dest='download_dir', + metavar='dir', + default=None, + help=("Download packages into instead of installing them, " + "regardless of what's already installed."), + ) + + cmd_opts.add_option(cmdoptions.src()) + + cmd_opts.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all specified packages to the newest available ' + 'version. This process is recursive regardless of whether ' + 'a dependency is already satisfied.' + ) + + cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='When upgrading, reinstall all packages even if they are ' + 'already up-to-date.') + + cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + default=default_user, + help='Ignore the installed packages (reinstalling instead).') + + cmd_opts.add_option(cmdoptions.no_deps()) + + cmd_opts.add_option(cmdoptions.install_options()) + cmd_opts.add_option(cmdoptions.global_options()) + + cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + default=default_user, + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.) On Debian systems, this is the " + "default when running outside of a virtual environment " + "and not as root.") + + cmd_opts.add_option( + '--system', + dest='use_user_site', + action='store_false', + help="Install using the system scheme (overrides --user on " + "Debian systems)") + + cmd_opts.add_option( + '--egg', + dest='as_egg', + action='store_true', + help="Install packages as eggs, not 'flat', like pip normally " + "does. This option is not about installing *from* eggs. " + "(WARNING: Because this option overrides pip's normal install" + " logic, requirements files may not behave as expected.)") + + cmd_opts.add_option( + '--root', + dest='root_path', + metavar='dir', + default=None, + help="Install everything relative to this alternate root " + "directory.") + + cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") + + cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile py files to pyc", + ) + + cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile py files to pyc", + ) + + cmd_opts.add_option(cmdoptions.use_wheel()) + cmd_opts.add_option(cmdoptions.no_use_wheel()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.pre()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def run(self, options, args): + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.download_dir: + warnings.warn( + "pip install --download has been deprecated and will be " + "removed in the future. Pip now has a download command that " + "should be used instead.", + RemovedInPip10Warning, + ) + options.ignore_installed = True + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_dir) + install_options = options.install_options or [] + if options.use_user_site: + if options.prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + install_options.append('--user') + install_options.append('--prefix=') + + temp_target_dir = None + if options.target_dir: + options.ignore_installed = True + temp_target_dir = tempfile.mkdtemp() + options.target_dir = os.path.abspath(options.target_dir) + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) + install_options.append('--home=' + temp_target_dir) + + global_options = options.global_options or [] + + with self._build_session(options) as session: + + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + if options.cache_dir and not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "by the current user and caching wheels has been " + "disabled. check the permissions and owner of that " + "directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=options.download_dir, + upgrade=options.upgrade, + as_egg=options.as_egg, + ignore_installed=options.ignore_installed, + ignore_dependencies=options.ignore_dependencies, + force_reinstall=options.force_reinstall, + use_user_site=options.use_user_site, + target_dir=temp_target_dir, + session=session, + pycompile=options.compile, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + require_hashes=options.require_hashes, + ) + + self.populate_requirement_set( + requirement_set, args, options, finder, session, self.name, + wheel_cache + ) + + if not requirement_set.has_requirements: + return + + try: + if (options.download_dir or not wheel or not + options.cache_dir): + # on -d don't do complex things like building + # wheels, and don't try to build wheels when wheel is + # not installed. + requirement_set.prepare_files(finder) + else: + # build wheels before install. + wb = WheelBuilder( + requirement_set, + finder, + build_options=[], + global_options=[], + ) + # Ignore the result: a failed wheel will be + # installed from the sdist/vcs whatever. + wb.build(autobuilding=True) + + if not options.download_dir: + requirement_set.install( + install_options, + global_options, + root=options.root_path, + prefix=options.prefix_path, + ) + reqs = sorted( + requirement_set.successfully_installed, + key=operator.attrgetter('name')) + items = [] + for req in reqs: + item = req.name + try: + if hasattr(req, 'installed_version'): + if req.installed_version: + item += '-' + req.installed_version + except Exception: + pass + items.append(item) + installed = ' '.join(items) + if installed: + logger.info('Successfully installed %s', installed) + else: + downloaded = ' '.join([ + req.name + for req in requirement_set.successfully_downloaded + ]) + if downloaded: + logger.info( + 'Successfully downloaded %s', downloaded + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + + if options.target_dir: + ensure_dir(options.target_dir) + + lib_dir = distutils_scheme('', home=temp_target_dir)['purelib'] + + for item in os.listdir(lib_dir): + target_item_dir = os.path.join(options.target_dir, item) + if os.path.exists(target_item_dir): + if not options.upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. Pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) + shutil.rmtree(temp_target_dir) + return requirement_set diff --git a/venv/lib/python3.5/site-packages/pip/commands/list.py b/venv/lib/python3.5/site-packages/pip/commands/list.py new file mode 100644 index 0000000..5346488 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/list.py @@ -0,0 +1,209 @@ +from __future__ import absolute_import + +import logging +import warnings + +from pip.basecommand import Command +from pip.exceptions import CommandError +from pip.index import PackageFinder +from pip.utils import ( + get_installed_distributions, dist_is_editable) +from pip.utils.deprecation import RemovedInPip10Warning +from pip.cmdoptions import make_option_group, index_group + + +logger = logging.getLogger(__name__) + + +class ListCommand(Command): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + name = 'list' + usage = """ + %prog [options]""" + summary = 'List installed packages.' + + def __init__(self, *args, **kw): + super(ListCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-o', '--outdated', + action='store_true', + default=False, + help='List outdated packages') + cmd_opts.add_option( + '-u', '--uptodate', + action='store_true', + default=False, + help='List uptodate packages') + cmd_opts.add_option( + '-e', '--editable', + action='store_true', + default=False, + help='List editable projects.') + cmd_opts.add_option( + '-l', '--local', + action='store_true', + default=False, + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + index_opts = make_option_group(index_group, self.parser) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def _build_package_finder(self, options, index_urls, session): + """ + Create a package finder appropriate to this list command. + """ + return PackageFinder( + find_links=options.find_links, + index_urls=index_urls, + allow_all_prereleases=options.pre, + trusted_hosts=options.trusted_hosts, + process_dependency_links=options.process_dependency_links, + session=session, + ) + + def run(self, options, args): + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + if options.outdated: + self.run_outdated(options) + elif options.uptodate: + self.run_uptodate(options) + else: + self.run_listing(options) + + def run_outdated(self, options): + for dist, latest_version, typ in sorted( + self.find_packages_latest_versions(options), + key=lambda p: p[0].project_name.lower()): + if latest_version > dist.parsed_version: + logger.info( + '%s - Latest: %s [%s]', + self.output_package(dist), latest_version, typ, + ) + + def find_packages_latest_versions(self, options): + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.info('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + dependency_links = [] + for dist in get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable): + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt'), + ) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, index_urls, session) + finder.add_dependency_links(dependency_links) + + installed_packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + ) + for dist in installed_packages: + typ = 'unknown' + all_candidates = finder.find_all_candidates(dist.key) + if not options.pre: + # Remove prereleases + all_candidates = [candidate for candidate in all_candidates + if not candidate.version.is_prerelease] + + if not all_candidates: + continue + best_candidate = max(all_candidates, + key=finder._candidate_sort_key) + remote_version = best_candidate.version + if best_candidate.location.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + yield dist, remote_version, typ + + def run_listing(self, options): + installed_packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + ) + self.output_package_listing(installed_packages) + + def output_package(self, dist): + if dist_is_editable(dist): + return '%s (%s, %s)' % ( + dist.project_name, + dist.version, + dist.location, + ) + else: + return '%s (%s)' % (dist.project_name, dist.version) + + def output_package_listing(self, installed_packages): + installed_packages = sorted( + installed_packages, + key=lambda dist: dist.project_name.lower(), + ) + for dist in installed_packages: + logger.info(self.output_package(dist)) + + def run_uptodate(self, options): + uptodate = [] + for dist, version, typ in self.find_packages_latest_versions(options): + if dist.parsed_version == version: + uptodate.append(dist) + self.output_package_listing(uptodate) diff --git a/venv/lib/python3.5/site-packages/pip/commands/search.py b/venv/lib/python3.5/site-packages/pip/commands/search.py new file mode 100644 index 0000000..3155e18 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/search.py @@ -0,0 +1,146 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap + +from pip.basecommand import Command, SUCCESS +from pip.download import PipXmlrpcTransport +from pip.models import PyPI +from pip.utils import get_terminal_size +from pip.utils.logging import indent_log +from pip.exceptions import CommandError +from pip.status_codes import NO_MATCHES_FOUND +from pip._vendor import pkg_resources +from pip._vendor.six.moves import xmlrpc_client + + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command): + """Search for PyPI packages whose name or summary contains .""" + name = 'search' + usage = """ + %prog [options] """ + summary = 'Search PyPI for packages.' + + def __init__(self, *args, **kw): + super(SearchCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '--index', + dest='index', + metavar='URL', + default=PyPI.pypi_url, + help='Base URL of Python Package Index (default %default)') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + raise CommandError('Missing required argument (search query).') + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query, options): + index_url = options.index + with self._build_session(options) as session: + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits + + +def transform_hits(hits): + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = {} + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + score = hit['_pypi_ordering'] + if score is None: + score = 0 + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + 'score': score, + } + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + packages[name]['score'] = score + + # each record has a unique name now, so we will convert the dict into a + # list sorted by score + package_list = sorted( + packages.values(), + key=lambda x: x['score'], + reverse=True, + ) + return package_list + + +def print_results(hits, name_column_width=None, terminal_width=None): + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(hit.get('versions', ['-'])[-1]) + for hit in hits + ]) + 4 + + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + version = hit.get('versions', ['-'])[-1] + if terminal_width is not None: + # wrap and indent summary to fit terminal + summary = textwrap.wrap( + summary, + terminal_width - name_column_width - 5, + ) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + + line = '%-*s - %s' % (name_column_width, + '%s (%s)' % (name, version), summary) + try: + logger.info(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + with indent_log(): + latest = highest_version(hit['versions']) + if dist.version == latest: + logger.info('INSTALLED: %s (latest)', dist.version) + else: + logger.info('INSTALLED: %s', dist.version) + logger.info('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + return next(iter( + sorted(versions, key=pkg_resources.parse_version, reverse=True) + )) diff --git a/venv/lib/python3.5/site-packages/pip/commands/show.py b/venv/lib/python3.5/site-packages/pip/commands/show.py new file mode 100644 index 0000000..52a673a --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/show.py @@ -0,0 +1,154 @@ +from __future__ import absolute_import + +from email.parser import FeedParser +import logging +import os + +from pip.basecommand import Command +from pip.status_codes import SUCCESS, ERROR +from pip._vendor import pkg_resources + + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """Show information about one or more installed packages.""" + name = 'show' + usage = """ + %prog [options] ...""" + summary = 'Show information about installed packages.' + + def __init__(self, *args, **kw): + super(ShowCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-f', '--files', + dest='files', + action='store_true', + default=False, + help='Show the full list of installed files for each package.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + if not args: + logger.warning('ERROR: Please provide a package name or names.') + return ERROR + query = args + + results = search_packages_info(query) + if not print_results(results, options.files): + return ERROR + return SUCCESS + + +def search_packages_info(query): + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + installed = dict( + [(p.project_name.lower(), p) for p in pkg_resources.working_set]) + query_names = [name.lower() for name in query] + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + } + file_list = None + metadata = None + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [l.split(',')[0] for l in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + installer = None + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + installer = line.strip() + break + package['installer'] = installer + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser can not deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if not line: + break + # Classifier: License :: OSI Approved :: MIT License + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_all_files): + """ + Print the informations from installed distributions found. + """ + results_printed = False + for dist in distributions: + results_printed = True + logger.info("---") + logger.info("Metadata-Version: %s", dist.get('metadata-version')) + logger.info("Name: %s", dist['name']) + logger.info("Version: %s", dist['version']) + logger.info("Summary: %s", dist.get('summary')) + logger.info("Home-page: %s", dist.get('home-page')) + logger.info("Author: %s", dist.get('author')) + logger.info("Author-email: %s", dist.get('author-email')) + if dist['installer'] is not None: + logger.info("Installer: %s", dist['installer']) + logger.info("License: %s", dist.get('license')) + logger.info("Location: %s", dist['location']) + logger.info("Requires: %s", ', '.join(dist['requires'])) + logger.info("Classifiers:") + for classifier in dist['classifiers']: + logger.info(" %s", classifier) + if list_all_files: + logger.info("Files:") + if 'files' in dist: + for line in dist['files']: + logger.info(" %s", line.strip()) + else: + logger.info("Cannot locate installed-files.txt") + if 'entry_points' in dist: + logger.info("Entry-points:") + for line in dist['entry_points']: + logger.info(" %s", line.strip()) + return results_printed diff --git a/venv/lib/python3.5/site-packages/pip/commands/uninstall.py b/venv/lib/python3.5/site-packages/pip/commands/uninstall.py new file mode 100644 index 0000000..8ba1a7c --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/uninstall.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import + +import pip +from pip.wheel import WheelCache +from pip.req import InstallRequirement, RequirementSet, parse_requirements +from pip.basecommand import Command +from pip.exceptions import InstallationError + + +class UninstallCommand(Command): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + name = 'uninstall' + usage = """ + %prog [options] ... + %prog [options] -r ...""" + summary = 'Uninstall packages.' + + def __init__(self, *args, **kw): + super(UninstallCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) + self.cmd_opts.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + with self._build_session(options) as session: + format_control = pip.index.FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + requirement_set = RequirementSet( + build_dir=None, + src_dir=None, + download_dir=None, + isolated=options.isolated_mode, + session=session, + wheel_cache=wheel_cache, + ) + for name in args: + requirement_set.add_requirement( + InstallRequirement.from_line( + name, isolated=options.isolated_mode, + wheel_cache=wheel_cache + ) + ) + for filename in options.requirements: + for req in parse_requirements( + filename, + options=options, + session=session, + wheel_cache=wheel_cache): + requirement_set.add_requirement(req) + if not requirement_set.has_requirements: + raise InstallationError( + 'You must give at least one requirement to %(name)s (see ' + '"pip help %(name)s")' % dict(name=self.name) + ) + requirement_set.uninstall(auto_confirm=options.yes) diff --git a/venv/lib/python3.5/site-packages/pip/commands/wheel.py b/venv/lib/python3.5/site-packages/pip/commands/wheel.py new file mode 100644 index 0000000..1d77fe6 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/commands/wheel.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import os +import warnings + +from pip.basecommand import RequirementCommand +from pip.exceptions import CommandError, PreviousBuildDirError +from pip.req import RequirementSet +from pip.utils import import_or_raise +from pip.utils.build import BuildDirectory +from pip.utils.deprecation import RemovedInPip10Warning +from pip.wheel import WheelCache, WheelBuilder +from pip import cmdoptions + + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: http://wheel.readthedocs.org/en/latest. + + Requirements: setuptools>=0.8, and wheel. + + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. + + """ + + name = 'wheel' + usage = """ + %prog [options] ... + %prog [options] -r ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + summary = 'Build wheels from your requirements.' + + def __init__(self, *args, **kw): + super(WheelCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option( + '-w', '--wheel-dir', + dest='wheel_dir', + metavar='dir', + default=os.curdir, + help=("Build wheels into , where the default is the " + "current working directory."), + ) + cmd_opts.add_option(cmdoptions.use_wheel()) + cmd_opts.add_option(cmdoptions.no_use_wheel()) + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option( + '--build-option', + dest='build_options', + metavar='options', + action='append', + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.") + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.editable()) + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.src()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.build_dir()) + + cmd_opts.add_option( + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the 'bdist_wheel' command.") + + cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, cmd_opts) + + def check_required_packages(self): + import_or_raise( + 'wheel.bdist_wheel', + CommandError, + "'pip wheel' requires the 'wheel' package. To fix this, run: " + "pip install wheel" + ) + pkg_resources = import_or_raise( + 'pkg_resources', + CommandError, + "'pip wheel' requires setuptools >= 0.8 for dist-info support." + " To fix this, run: pip install --upgrade setuptools" + ) + if not hasattr(pkg_resources, 'DistInfoDistribution'): + raise CommandError( + "'pip wheel' requires setuptools >= 0.8 for dist-info " + "support. To fix this, run: pip install --upgrade " + "setuptools" + ) + + def run(self, options, args): + self.check_required_packages() + cmdoptions.resolve_wheel_no_use_binary(options) + cmdoptions.check_install_build_global(options) + + if options.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if options.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.info('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + with BuildDirectory(options.build_dir, + delete=build_delete) as build_dir: + requirement_set = RequirementSet( + build_dir=build_dir, + src_dir=options.src_dir, + download_dir=None, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=True, + isolated=options.isolated_mode, + session=session, + wheel_cache=wheel_cache, + wheel_download_dir=options.wheel_dir, + require_hashes=options.require_hashes + ) + + self.populate_requirement_set( + requirement_set, args, options, finder, session, self.name, + wheel_cache + ) + + if not requirement_set.has_requirements: + return + + try: + # build wheels + wb = WheelBuilder( + requirement_set, + finder, + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + if not wb.build(): + raise CommandError( + "Failed to build one or more wheels" + ) + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + if not options.no_clean: + requirement_set.cleanup_files() diff --git a/venv/lib/python3.5/site-packages/pip/compat/__init__.py b/venv/lib/python3.5/site-packages/pip/compat/__init__.py new file mode 100644 index 0000000..703852b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/compat/__init__.py @@ -0,0 +1,164 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" +from __future__ import absolute_import, division + +import os +import sys + +from pip._vendor.six import text_type + +try: + from logging.config import dictConfig as logging_dictConfig +except ImportError: + from pip.compat.dictconfig import dictConfig as logging_dictConfig + +try: + from collections import OrderedDict +except ImportError: + from pip.compat.ordereddict import OrderedDict + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress + except ImportError: + import ipaddr as ipaddress + ipaddress.ip_address = ipaddress.IPAddress + ipaddress.ip_network = ipaddress.IPNetwork + + +try: + import sysconfig + + def get_stdlib(): + paths = [ + sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib"), + ] + return set(filter(bool, paths)) +except ImportError: + from distutils import sysconfig + + def get_stdlib(): + paths = [ + sysconfig.get_python_lib(standard_lib=True), + sysconfig.get_python_lib(standard_lib=True, plat_specific=True), + ] + return set(filter(bool, paths)) + + +__all__ = [ + "logging_dictConfig", "ipaddress", "uses_pycache", "console_to_str", + "native_str", "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", + "OrderedDict", +] + + +if sys.version_info >= (3, 4): + uses_pycache = True + from importlib.util import cache_from_source +else: + import imp + uses_pycache = hasattr(imp, 'cache_from_source') + if uses_pycache: + cache_from_source = imp.cache_from_source + else: + cache_from_source = None + + +if sys.version_info >= (3,): + def console_to_str(s): + try: + return s.decode(sys.__stdout__.encoding) + except UnicodeDecodeError: + return s.decode('utf_8') + + def native_str(s, replace=False): + if isinstance(s, bytes): + return s.decode('utf-8', 'replace' if replace else 'strict') + return s + +else: + def console_to_str(s): + return s + + def native_str(s, replace=False): + # Replace is ignored -- unicode to UTF-8 can't fail + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +def total_seconds(td): + if hasattr(td, "total_seconds"): + return td.total_seconds() + else: + val = td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6 + return val / 10 ** 6 + + +def get_path_uid(path): + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerabity, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "%s is a symlink; Will not return uid for symlinks" % path + ) + return file_uid + + +def expanduser(path): + """ + Expand ~ and ~user constructions. + + Includes a workaround for http://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = ('python', 'wsgiref') +if sys.version_info >= (2, 7): + stdlib_pkgs += ('argparse',) + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 diff --git a/venv/lib/python3.5/site-packages/pip/compat/dictconfig.py b/venv/lib/python3.5/site-packages/pip/compat/dictconfig.py new file mode 100644 index 0000000..ec684aa --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/compat/dictconfig.py @@ -0,0 +1,565 @@ +# This is a copy of the Python logging.config.dictconfig module, +# reproduced with permission. It is provided here for backwards +# compatibility for Python versions prior to 2.7. +# +# Copyright 2009-2010 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +from __future__ import absolute_import + +import logging.handlers +import re +import sys +import types + +from pip._vendor import six + +# flake8: noqa + +IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) + + +def valid_ident(s): + m = IDENTIFIER.match(s) + if not m: + raise ValueError('Not a valid Python identifier: %r' % s) + return True + +# +# This function is defined in logging only in recent versions of Python +# +try: + from logging import _checkLevel +except ImportError: + def _checkLevel(level): + if isinstance(level, int): + rv = level + elif str(level) == level: + if level not in logging._levelNames: + raise ValueError('Unknown level: %r' % level) + rv = logging._levelNames[level] + else: + raise TypeError('Level not an integer or a ' + 'valid string: %r' % level) + return rv + +# The ConvertingXXX classes are wrappers around standard Python containers, +# and they serve to convert any suitable values in the container. The +# conversion converts base dicts, lists and tuples to their wrapped +# equivalents, whereas strings which match a conversion format are converted +# appropriately. +# +# Each wrapper should have a configurator attribute holding the actual +# configurator to use for conversion. + + +class ConvertingDict(dict): + """A converting dictionary wrapper.""" + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def get(self, key, default=None): + value = dict.get(self, key, default) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, key, default=None): + value = dict.pop(self, key, default) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + +class ConvertingList(list): + """A converting list wrapper.""" + def __getitem__(self, key): + value = list.__getitem__(self, key) + result = self.configurator.convert(value) + # If the converted value is different, save for next time + if value is not result: + self[key] = result + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + def pop(self, idx=-1): + value = list.pop(self, idx) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + return result + + +class ConvertingTuple(tuple): + """A converting tuple wrapper.""" + def __getitem__(self, key): + value = tuple.__getitem__(self, key) + result = self.configurator.convert(value) + if value is not result: + if type(result) in (ConvertingDict, ConvertingList, + ConvertingTuple): + result.parent = self + result.key = key + return result + + +class BaseConfigurator(object): + """ + The configurator base class which defines some useful defaults. + """ + + CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = __import__ + + def __init__(self, config): + self.config = ConvertingDict(config) + self.config.configurator = self + + def resolve(self, s): + """ + Resolve strings to objects using standard import and attribute + syntax. + """ + name = s.split('.') + used = name.pop(0) + try: + found = self.importer(used) + for frag in name: + used += '.' + frag + try: + found = getattr(found, frag) + except AttributeError: + self.importer(used) + found = getattr(found, frag) + return found + except ImportError: + e, tb = sys.exc_info()[1:] + v = ValueError('Cannot resolve %r: %s' % (s, e)) + v.__cause__, v.__traceback__ = e, tb + raise v + + def ext_convert(self, value): + """Default converter for the ext:// protocol.""" + return self.resolve(value) + + def cfg_convert(self, value): + """Default converter for the cfg:// protocol.""" + rest = value + m = self.WORD_PATTERN.match(rest) + if m is None: + raise ValueError("Unable to convert %r" % value) + else: + rest = rest[m.end():] + d = self.config[m.groups()[0]] + # print d, rest + while rest: + m = self.DOT_PATTERN.match(rest) + if m: + d = d[m.groups()[0]] + else: + m = self.INDEX_PATTERN.match(rest) + if m: + idx = m.groups()[0] + if not self.DIGIT_PATTERN.match(idx): + d = d[idx] + else: + try: + n = int(idx) # try as number first (most likely) + d = d[n] + except TypeError: + d = d[idx] + if m: + rest = rest[m.end():] + else: + raise ValueError('Unable to convert ' + '%r at %r' % (value, rest)) + # rest should be empty + return d + + def convert(self, value): + """ + Convert values to an appropriate type. dicts, lists and tuples are + replaced by their converting alternatives. Strings are checked to + see if they have a conversion format and are converted if they do. + """ + if not isinstance(value, ConvertingDict) and isinstance(value, dict): + value = ConvertingDict(value) + value.configurator = self + elif not isinstance(value, ConvertingList) and isinstance(value, list): + value = ConvertingList(value) + value.configurator = self + elif not isinstance(value, ConvertingTuple) and\ + isinstance(value, tuple): + value = ConvertingTuple(value) + value.configurator = self + elif isinstance(value, six.string_types): # str for py3k + m = self.CONVERT_PATTERN.match(value) + if m: + d = m.groupdict() + prefix = d['prefix'] + converter = self.value_converters.get(prefix, None) + if converter: + suffix = d['suffix'] + converter = getattr(self, converter) + value = converter(suffix) + return value + + def configure_custom(self, config): + """Configure an object with a user-supplied factory.""" + c = config.pop('()') + if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + kwargs = dict((k, config[k]) for k in config if valid_ident(k)) + result = c(**kwargs) + if props: + for name, value in props.items(): + setattr(result, name, value) + return result + + def as_tuple(self, value): + """Utility function which converts lists to tuples.""" + if isinstance(value, list): + value = tuple(value) + return value + + +class DictConfigurator(BaseConfigurator): + """ + Configure logging using a dictionary-like object to describe the + configuration. + """ + + def configure(self): + """Do the configuration.""" + + config = self.config + if 'version' not in config: + raise ValueError("dictionary doesn't specify a version") + if config['version'] != 1: + raise ValueError("Unsupported version: %s" % config['version']) + incremental = config.pop('incremental', False) + EMPTY_DICT = {} + logging._acquireLock() + try: + if incremental: + handlers = config.get('handlers', EMPTY_DICT) + # incremental handler config only if handler name + # ties in to logging._handlers (Python 2.7) + if sys.version_info[:2] == (2, 7): + for name in handlers: + if name not in logging._handlers: + raise ValueError('No handler found with ' + 'name %r' % name) + else: + try: + handler = logging._handlers[name] + handler_config = handlers[name] + level = handler_config.get('level', None) + if level: + handler.setLevel(_checkLevel(level)) + except StandardError as e: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + loggers = config.get('loggers', EMPTY_DICT) + for name in loggers: + try: + self.configure_logger(name, loggers[name], True) + except StandardError as e: + raise ValueError('Unable to configure logger ' + '%r: %s' % (name, e)) + root = config.get('root', None) + if root: + try: + self.configure_root(root, True) + except StandardError as e: + raise ValueError('Unable to configure root ' + 'logger: %s' % e) + else: + disable_existing = config.pop('disable_existing_loggers', True) + + logging._handlers.clear() + del logging._handlerList[:] + + # Do formatters first - they don't refer to anything else + formatters = config.get('formatters', EMPTY_DICT) + for name in formatters: + try: + formatters[name] = self.configure_formatter( + formatters[name]) + except StandardError as e: + raise ValueError('Unable to configure ' + 'formatter %r: %s' % (name, e)) + # Next, do filters - they don't refer to anything else, either + filters = config.get('filters', EMPTY_DICT) + for name in filters: + try: + filters[name] = self.configure_filter(filters[name]) + except StandardError as e: + raise ValueError('Unable to configure ' + 'filter %r: %s' % (name, e)) + + # Next, do handlers - they refer to formatters and filters + # As handlers can refer to other handlers, sort the keys + # to allow a deterministic order of configuration + handlers = config.get('handlers', EMPTY_DICT) + for name in sorted(handlers): + try: + handler = self.configure_handler(handlers[name]) + handler.name = name + handlers[name] = handler + except StandardError as e: + raise ValueError('Unable to configure handler ' + '%r: %s' % (name, e)) + # Next, do loggers - they refer to handlers and filters + + # we don't want to lose the existing loggers, + # since other threads may have pointers to them. + # existing is set to contain all existing loggers, + # and as we go through the new configuration we + # remove any which are configured. At the end, + # what's left in existing is the set of loggers + # which were in the previous configuration but + # which are not in the new configuration. + root = logging.root + existing = list(root.manager.loggerDict) + # The list needs to be sorted so that we can + # avoid disabling child loggers of explicitly + # named loggers. With a sorted list it is easier + # to find the child loggers. + existing.sort() + # We'll keep the list of existing loggers + # which are children of named loggers here... + child_loggers = [] + # now set up the new ones... + loggers = config.get('loggers', EMPTY_DICT) + for name in loggers: + if name in existing: + i = existing.index(name) + prefixed = name + "." + pflen = len(prefixed) + num_existing = len(existing) + i = i + 1 # look at the entry after name + while (i < num_existing) and\ + (existing[i][:pflen] == prefixed): + child_loggers.append(existing[i]) + i = i + 1 + existing.remove(name) + try: + self.configure_logger(name, loggers[name]) + except StandardError as e: + raise ValueError('Unable to configure logger ' + '%r: %s' % (name, e)) + + # Disable any old loggers. There's no point deleting + # them as other threads may continue to hold references + # and by disabling them, you stop them doing any logging. + # However, don't disable children of named loggers, as that's + # probably not what was intended by the user. + for log in existing: + logger = root.manager.loggerDict[log] + if log in child_loggers: + logger.level = logging.NOTSET + logger.handlers = [] + logger.propagate = True + elif disable_existing: + logger.disabled = True + + # And finally, do the root logger + root = config.get('root', None) + if root: + try: + self.configure_root(root) + except StandardError as e: + raise ValueError('Unable to configure root ' + 'logger: %s' % e) + finally: + logging._releaseLock() + + def configure_formatter(self, config): + """Configure a formatter from a dictionary.""" + if '()' in config: + factory = config['()'] # for use in exception handler + try: + result = self.configure_custom(config) + except TypeError as te: + if "'format'" not in str(te): + raise + # Name of parameter changed from fmt to format. + # Retry with old name. + # This is so that code can be used with older Python versions + #(e.g. by Django) + config['fmt'] = config.pop('format') + config['()'] = factory + result = self.configure_custom(config) + else: + fmt = config.get('format', None) + dfmt = config.get('datefmt', None) + result = logging.Formatter(fmt, dfmt) + return result + + def configure_filter(self, config): + """Configure a filter from a dictionary.""" + if '()' in config: + result = self.configure_custom(config) + else: + name = config.get('name', '') + result = logging.Filter(name) + return result + + def add_filters(self, filterer, filters): + """Add filters to a filterer from a list of names.""" + for f in filters: + try: + filterer.addFilter(self.config['filters'][f]) + except StandardError as e: + raise ValueError('Unable to add filter %r: %s' % (f, e)) + + def configure_handler(self, config): + """Configure a handler from a dictionary.""" + formatter = config.pop('formatter', None) + if formatter: + try: + formatter = self.config['formatters'][formatter] + except StandardError as e: + raise ValueError('Unable to set formatter ' + '%r: %s' % (formatter, e)) + level = config.pop('level', None) + filters = config.pop('filters', None) + if '()' in config: + c = config.pop('()') + if not hasattr(c, '__call__') and hasattr(types, 'ClassType') and type(c) != types.ClassType: + c = self.resolve(c) + factory = c + else: + klass = self.resolve(config.pop('class')) + # Special case for handler which refers to another handler + if issubclass(klass, logging.handlers.MemoryHandler) and\ + 'target' in config: + try: + config['target'] = self.config['handlers'][config['target']] + except StandardError as e: + raise ValueError('Unable to set target handler ' + '%r: %s' % (config['target'], e)) + elif issubclass(klass, logging.handlers.SMTPHandler) and\ + 'mailhost' in config: + config['mailhost'] = self.as_tuple(config['mailhost']) + elif issubclass(klass, logging.handlers.SysLogHandler) and\ + 'address' in config: + config['address'] = self.as_tuple(config['address']) + factory = klass + kwargs = dict((k, config[k]) for k in config if valid_ident(k)) + try: + result = factory(**kwargs) + except TypeError as te: + if "'stream'" not in str(te): + raise + # The argument name changed from strm to stream + # Retry with old name. + # This is so that code can be used with older Python versions + #(e.g. by Django) + kwargs['strm'] = kwargs.pop('stream') + result = factory(**kwargs) + if formatter: + result.setFormatter(formatter) + if level is not None: + result.setLevel(_checkLevel(level)) + if filters: + self.add_filters(result, filters) + return result + + def add_handlers(self, logger, handlers): + """Add handlers to a logger from a list of names.""" + for h in handlers: + try: + logger.addHandler(self.config['handlers'][h]) + except StandardError as e: + raise ValueError('Unable to add handler %r: %s' % (h, e)) + + def common_logger_config(self, logger, config, incremental=False): + """ + Perform configuration which is common to root and non-root loggers. + """ + level = config.get('level', None) + if level is not None: + logger.setLevel(_checkLevel(level)) + if not incremental: + # Remove any existing handlers + for h in logger.handlers[:]: + logger.removeHandler(h) + handlers = config.get('handlers', None) + if handlers: + self.add_handlers(logger, handlers) + filters = config.get('filters', None) + if filters: + self.add_filters(logger, filters) + + def configure_logger(self, name, config, incremental=False): + """Configure a non-root logger from a dictionary.""" + logger = logging.getLogger(name) + self.common_logger_config(logger, config, incremental) + propagate = config.get('propagate', None) + if propagate is not None: + logger.propagate = propagate + + def configure_root(self, config, incremental=False): + """Configure a root logger from a dictionary.""" + root = logging.getLogger() + self.common_logger_config(root, config, incremental) + +dictConfigClass = DictConfigurator + + +def dictConfig(config): + """Configure logging using a dictionary.""" + dictConfigClass(config).configure() diff --git a/venv/lib/python3.5/site-packages/pip/compat/ordereddict.py b/venv/lib/python3.5/site-packages/pip/compat/ordereddict.py new file mode 100644 index 0000000..6eb3ba4 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/compat/ordereddict.py @@ -0,0 +1,129 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +# flake8: noqa + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff --git a/venv/lib/python3.5/site-packages/pip/download.py b/venv/lib/python3.5/site-packages/pip/download.py new file mode 100644 index 0000000..bbef9ea --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/download.py @@ -0,0 +1,895 @@ +from __future__ import absolute_import + +import cgi +import email.utils +import getpass +import json +import logging +import mimetypes +import os +import platform +import re +import shutil +import sys +import tempfile + +try: + import ssl # noqa + HAS_TLS = True +except ImportError: + HAS_TLS = False + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +import pip + +from pip.exceptions import InstallationError, HashMismatch +from pip.models import PyPI +from pip.utils import (splitext, rmtree, format_size, display_path, + backup_dir, ask_path_exists, unpack_file, + ARCHIVE_EXTENSIONS, consume, call_subprocess) +from pip.utils.encoding import auto_decode +from pip.utils.filesystem import check_path_owner +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner +from pip.locations import write_delete_marker_file +from pip.vcs import vcs +from pip._vendor import requests, six +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.requests.packages import urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.lockfile import LockError +from pip._vendor.six.moves import xmlrpc_client + + +__all__ = ['get_file_content', + 'is_url', 'url_to_path', 'path_to_url', + 'is_archive_file', 'unpack_vcs_link', + 'unpack_file_url', 'is_vcs_url', 'is_file_url', + 'unpack_http_url', 'unpack_url'] + + +logger = logging.getLogger(__name__) + + +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": pip.__version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + distro = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], platform.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], platform.libc_ver()), + )) + if libc: + distro["libc"] = libc + if distro: + data["distro"] = distro + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "OS X", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + # Python 2.6 doesn't have ssl.OPENSSL_VERSION. + if HAS_TLS and sys.version_info[:2] > (2, 6): + data["openssl_version"] = ssl.OPENSSL_VERSION + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True): + self.prompting = prompting + self.passwords = {} + + def __call__(self, req): + parsed = urllib_parse.urlparse(req.url) + + # Get the netloc without any embedded credentials + netloc = parsed.netloc.rsplit("@", 1)[-1] + + # Set the url of the request to the url without any credentials + req.url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:]) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + # Extract credentials embedded in the url if we have none stored + if username is None: + username, password = self.parse_credentials(parsed.netloc) + + if username or password: + # Store the username and password + self.passwords[netloc] = (username, password) + + # Send the basic auth with this request + req = HTTPBasicAuth(username or "", password or "")(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + def handle_401(self, resp, **kwargs): + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + # We are not able to prompt the user so simple return the response + if not self.prompting: + return resp + + parsed = urllib_parse.urlparse(resp.url) + + # Prompt the user for a new username and password + username = six.moves.input("User for %s: " % parsed.netloc) + password = getpass.getpass("Password: ") + + # Store the new username and password to use for future requests + if username or password: + self.passwords[parsed.netloc] = (username, password) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def parse_credentials(self, netloc): + if "@" in netloc: + userinfo = netloc.rsplit("@", 1)[0] + if ":" in userinfo: + return userinfo.split(":", 1) + return userinfo, None + return None, None + + +class LocalFSAdapter(BaseAdapter): + + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self): + pass + + +class SafeFileCache(FileCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, *args, **kwargs): + super(SafeFileCache, self).__init__(*args, **kwargs) + + # Check to ensure that the directory containing our cache directory + # is owned by the user current executing pip. If it does not exist + # we will check the parent directory until we find one that does exist. + # If it is not owned by the user executing pip then we will disable + # the cache and log a warning. + if not check_path_owner(self.directory): + logger.warning( + "The directory '%s' or its parent directory is not owned by " + "the current user and the cache has been disabled. Please " + "check the permissions and owner of that directory. If " + "executing pip with sudo, you may want sudo's -H flag.", + self.directory, + ) + + # Set our directory to None to disable the Cache + self.directory = None + + def get(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).get(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def set(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).set(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + def delete(self, *args, **kwargs): + # If we don't have a directory, then the cache should be a no-op. + if self.directory is None: + return + + try: + return super(SafeFileCache, self).delete(*args, **kwargs) + except (LockError, OSError, IOError): + # We intentionally silence this error, if we can't access the cache + # then we can just skip caching and process the request as if + # caching wasn't enabled. + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + + +class PipSession(requests.Session): + + timeout = None + + def __init__(self, *args, **kwargs): + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + insecure_hosts = kwargs.pop("insecure_hosts", []) + + super(PipSession, self).__init__(*args, **kwargs) + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth() + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + status_forcelist=[503], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # We want to _only_ cache responses on securely fetched origins. We do + # this because we can't validate the response of an insecurely fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache, use_dir_lock=True), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching (see above) so we'll use it for all http:// URLs as + # well as any https:// host that we've marked as ignoring TLS errors + # for. + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + # We want to use a non-validating adapter for any requests which are + # deemed insecure. + for host in insecure_hosts: + self.mount("https://{0}/".format(host), insecure_adapter) + + def request(self, method, url, *args, **kwargs): + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + + # Dispatch the actual request + return super(PipSession, self).request(method, url, *args, **kwargs) + + +def get_file_content(url, comes_from=None, session=None): + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode.""" + if session is None: + raise TypeError( + "get_file_content() missing 1 required keyword argument: 'session'" + ) + + match = _scheme_re.search(url) + if match: + scheme = match.group(1).lower() + if (scheme == 'file' and comes_from and + comes_from.startswith('http')): + raise InstallationError( + 'Requirements file %s references URL %s, which is local' + % (comes_from, url)) + if scheme == 'file': + path = url.split(':', 1)[1] + path = path.replace('\\', '/') + match = _url_slash_drive_re.match(path) + if match: + path = match.group(1) + ':' + path.split('|', 1)[1] + path = urllib_parse.unquote(path) + if path.startswith('/'): + path = '/' + path.lstrip('/') + url = path + else: + # FIXME: catch some errors + resp = session.get(url) + resp.raise_for_status() + return resp.url, resp.text + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: %s' % str(exc) + ) + return url, content + + +_scheme_re = re.compile(r'^(http|https|file):', re.I) +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) + + +def is_url(name): + """Returns true if the name looks like a URL""" + if ':' not in name: + return False + scheme = name.split(':', 1)[0].lower() + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def url_to_path(url): + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not %r)" % url) + + _, netloc, path, _, _ = urllib_parse.urlsplit(url) + + # if we have a UNC path, prepend UNC share notation + if netloc: + netloc = '\\\\' + netloc + + path = urllib_request.url2pathname(netloc + path) + return path + + +def path_to_url(path): + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url + + +def is_archive_file(name): + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False + + +def unpack_vcs_link(link, location): + vcs_backend = _get_used_vcs_backend(link) + vcs_backend.unpack(location) + + +def _get_used_vcs_backend(link): + for backend in vcs.backends: + if link.scheme in backend.schemes: + vcs_backend = backend(link.url) + return vcs_backend + + +def is_vcs_url(link): + return bool(_get_used_vcs_backend(link)) + + +def is_file_url(link): + return link.url.lower().startswith('file:') + + +def is_dir_url(link): + """Return whether a file:// Link points to a directory. + + ``link`` must not have any other scheme but file://. Call is_file_url() + first. + + """ + link_path = url_to_path(link.url_without_fragment) + return os.path.isdir(link_path) + + +def _progress_indicator(iterable, *args, **kwargs): + return iterable + + +def _download_url(resp, link, content_file, hashes): + try: + total_length = int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + total_length = 0 + + cached_resp = getattr(resp, "from_cache", False) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif cached_resp: + show_progress = False + elif total_length > (40 * 1000): + show_progress = True + elif not total_length: + show_progress = True + else: + show_progress = False + + show_url = link.show_url + + def resp_read(chunk_size): + try: + # Special case for urllib3. + for chunk in resp.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = resp.raw.read(chunk_size) + if not chunk: + break + yield chunk + + def written_chunks(chunks): + for chunk in chunks: + content_file.write(chunk) + yield chunk + + progress_indicator = _progress_indicator + + if link.netloc == PyPI.netloc: + url = show_url + else: + url = link.url_without_fragment + + if show_progress: # We don't show progress on cached responses + if total_length: + logger.info("Downloading %s (%s)", url, format_size(total_length)) + progress_indicator = DownloadProgressBar(max=total_length).iter + else: + logger.info("Downloading %s", url) + progress_indicator = DownloadProgressSpinner().iter + elif cached_resp: + logger.info("Using cached %s", url) + else: + logger.info("Downloading %s", url) + + logger.debug('Downloading from URL %s', link) + + downloaded_chunks = written_chunks( + progress_indicator( + resp_read(CONTENT_CHUNK_SIZE), + CONTENT_CHUNK_SIZE + ) + ) + if hashes: + hashes.check_against_chunks(downloaded_chunks) + else: + consume(downloaded_chunks) + + +def _copy_file(filename, location, link): + copy = True + download_location = os.path.join(location, link.filename) + if os.path.exists(download_location): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup ' % + display_path(download_location), ('i', 'w', 'b')) + if response == 'i': + copy = False + elif response == 'w': + logger.warning('Deleting %s', display_path(download_location)) + os.remove(download_location) + elif response == 'b': + dest_file = backup_dir(download_location) + logger.warning( + 'Backing up %s to %s', + display_path(download_location), + display_path(dest_file), + ) + shutil.move(download_location, dest_file) + if copy: + shutil.copy(filename, download_location) + logger.info('Saved %s', display_path(download_location)) + + +def unpack_http_url(link, location, download_dir=None, + session=None, hashes=None): + if session is None: + raise TypeError( + "unpack_http_url() missing 1 required keyword argument: 'session'" + ) + + temp_dir = tempfile.mkdtemp('-unpack', 'pip-') + + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = mimetypes.guess_type(from_path)[0] + else: + # let's download to a tmp dir + from_path, content_type = _download_http_url(link, + session, + temp_dir, + hashes) + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified; let's copy the archive there + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + if not already_downloaded_path: + os.unlink(from_path) + rmtree(temp_dir) + + +def unpack_file_url(link, location, download_dir=None, hashes=None): + """Unpack link into location. + + If download_dir is provided and link points to a file, make a copy + of the link file inside download_dir. + """ + link_path = url_to_path(link.url_without_fragment) + + # If it's a url to a local directory + if is_dir_url(link): + if os.path.isdir(location): + rmtree(location) + shutil.copytree(link_path, location, symlinks=True) + if download_dir: + logger.info('Link is a directory, ignoring download_dir') + return + + # If --require-hashes is off, `hashes` is either empty, the + # link's embeddded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(link_path) + + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, + download_dir, + hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link_path + + content_type = mimetypes.guess_type(from_path)[0] + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(from_path, location, content_type, link) + + # a download dir is specified and not already downloaded + if download_dir and not already_downloaded_path: + _copy_file(from_path, download_dir, link) + + +def _copy_dist_from_dir(link_path, location): + """Copy distribution files in `link_path` to `location`. + + Invoked when user requests to install a local directory. E.g.: + + pip install . + pip install ~/dev/git-repos/python-prompt-toolkit + + """ + + # Note: This is currently VERY SLOW if you have a lot of data in the + # directory, because it copies everything with `shutil.copytree`. + # What it should really do is build an sdist and install that. + # See https://github.com/pypa/pip/issues/2195 + + if os.path.isdir(location): + rmtree(location) + + # build an sdist + setup_py = 'setup.py' + sdist_args = [sys.executable] + sdist_args.append('-c') + sdist_args.append(SETUPTOOLS_SHIM % setup_py) + sdist_args.append('sdist') + sdist_args += ['--dist-dir', location] + logger.info('Running setup.py sdist for %s', link_path) + + with indent_log(): + call_subprocess(sdist_args, cwd=link_path, show_stdout=False) + + # unpack sdist into `location` + sdist = os.path.join(location, os.listdir(location)[0]) + logger.info('Unpacking sdist %s into %s', sdist, location) + unpack_file(sdist, location, content_type=None, link=None) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + def __init__(self, index_url, session, use_datetime=False): + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + response.raise_for_status() + self.verbose = verbose + return self.parse_response(response.raw) + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise + + +def unpack_url(link, location, download_dir=None, + only_download=False, session=None, hashes=None): + """Unpack link. + If link is a VCS link: + if only_download, export into download_dir and ignore location + else unpack into location + for other types of link: + - unpack into location + - if download_dir, copy the file into download_dir + - if only_download, mark location for deletion + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if is_vcs_url(link): + unpack_vcs_link(link, location) + + # file urls + elif is_file_url(link): + unpack_file_url(link, location, download_dir, hashes=hashes) + + # http urls + else: + if session is None: + session = PipSession() + + unpack_http_url( + link, + location, + download_dir, + session, + hashes=hashes + ) + if only_download: + write_delete_marker_file(location) + + +def _download_http_url(link, session, temp_dir, hashes): + """Download link url into temp_dir using provided session""" + target_url = link.url.split('#', 1)[0] + try: + resp = session.get( + target_url, + # We use Accept-Encoding: identity here because requests + # defaults to accepting compressed responses. This breaks in + # a variety of ways depending on how the server is configured. + # - Some servers will notice that the file isn't a compressible + # file and will leave the file alone and with an empty + # Content-Encoding + # - Some servers will notice that the file is already + # compressed and will leave the file alone and will add a + # Content-Encoding: gzip header + # - Some servers won't notice anything at all and will take + # a file that's already been compressed and compress it again + # and set the Content-Encoding: gzip header + # By setting this to request only the identity encoding We're + # hoping to eliminate the third case. Hopefully there does not + # exist a server which when given a file will notice it is + # already compressed and that you're not asking for a + # compressed file and will then decompress it before sending + # because if that's the case I don't think it'll ever be + # possible to make this work. + headers={"Accept-Encoding": "identity"}, + stream=True, + ) + resp.raise_for_status() + except requests.HTTPError as exc: + logger.critical( + "HTTP error %s while getting %s", exc.response.status_code, link, + ) + raise + + content_type = resp.headers.get('content-type', '') + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + type, params = cgi.parse_header(content_disposition) + # We use ``or`` here because we don't want to use an "empty" value + # from the filename param. + filename = params.get('filename') or filename + ext = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(content_type) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + file_path = os.path.join(temp_dir, filename) + with open(file_path, 'wb') as content_file: + _download_url(resp, link, content_file, hashes) + return file_path, content_type + + +def _check_download_dir(link, download_dir, hashes): + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + if os.path.exists(download_path): + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + return None diff --git a/venv/lib/python3.5/site-packages/pip/exceptions.py b/venv/lib/python3.5/site-packages/pip/exceptions.py new file mode 100644 index 0000000..a529e40 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/exceptions.py @@ -0,0 +1,239 @@ +"""Exceptions used throughout package""" +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + + +class PipError(Exception): + """Base pip exception""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + self.errors = [] + + def append(self, error): + self.errors.append(error) + + def __str__(self): + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + + def __nonzero__(self): + return bool(self.errors) + + def __bool__(self): + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None + head = '' + + def body(self): + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + populate_link() having already been called + + """ + return ' %s' % self._requirement_name() + + def __str__(self): + return '%s\n%s' % (self.head, self.body()) + + def _requirement_name(self): + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + from pip.utils.hashes import FAVORITE_HASH # Dodge circular import. + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' %s --hash=%s:%s' % (package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + return ' %s:\n%s' % (self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected %s %s' % (next(prefix), e)) + for e in expecteds) + lines.append(' Got %s\n' % + self.gots[hash_name].hexdigest()) + prefix = ' or' + return '\n'.join(lines) diff --git a/venv/lib/python3.5/site-packages/pip/index.py b/venv/lib/python3.5/site-packages/pip/index.py new file mode 100644 index 0000000..cae4c2d --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/index.py @@ -0,0 +1,1048 @@ +"""Routines related to PyPI, indexes""" +from __future__ import absolute_import + +import logging +import cgi +from collections import namedtuple +import itertools +import sys +import os +import re +import mimetypes +import posixpath +import warnings + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip.compat import ipaddress +from pip.utils import ( + cached_property, splitext, normalize_path, + ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, +) +from pip.utils.deprecation import RemovedInPip9Warning, RemovedInPip10Warning +from pip.utils.logging import indent_log +from pip.exceptions import ( + DistributionNotFound, BestVersionAlreadyInstalled, InvalidWheelFilename, + UnsupportedWheel, +) +from pip.download import HAS_TLS, is_url, path_to_url, url_to_path +from pip.wheel import Wheel, wheel_ext +from pip.pep425tags import supported_tags +from pip._vendor import html5lib, requests, six +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.requests.exceptions import SSLError + + +__all__ = ['FormatControl', 'fmt_ctl_handle_mutual_exclude', 'PackageFinder'] + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] + + +logger = logging.getLogger(__name__) + + +class InstallationCandidate(object): + + def __init__(self, project, version, location): + self.project = project + self.version = parse_version(version) + self.location = location + self._key = (self.project, self.version, self.location) + + def __repr__(self): + return "".format( + self.project, self.version, self.location, + ) + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, InstallationCandidate): + return NotImplemented + + return method(self._key, other._key) + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__(self, find_links, index_urls, allow_all_prereleases=False, + trusted_hosts=None, process_dependency_links=False, + session=None, format_control=None): + """Create a PackageFinder. + + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + """ + if session is None: + raise TypeError( + "PackageFinder() missing 1 required keyword argument: " + "'session'" + ) + + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + self.find_links = [] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + self.find_links.append(link) + + self.index_urls = index_urls + self.dependency_links = [] + + # These are boring links that have already been logged somehow: + self.logged_links = set() + + self.format_control = format_control or FormatControl(set(), set()) + + # Domains that we won't emit warnings for when not using HTTPS + self.secure_origins = [ + ("*", host, "*") + for host in (trusted_hosts if trusted_hosts else []) + ] + + # Do we want to allow _all_ pre-releases? + self.allow_all_prereleases = allow_all_prereleases + + # Do we process dependency links? + self.process_dependency_links = process_dependency_links + + # The Session we'll use to make requests + self.session = session + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not HAS_TLS: + for link in itertools.chain(self.index_urls, self.find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == "https": + logger.warning( + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." + ) + break + + def add_dependency_links(self, links): + # # FIXME: this shouldn't be global list this, it should only + # # apply to requirements of the package that specifies the + # # dependency_links value + # # FIXME: also, we should track comes_from (i.e., use Link) + if self.process_dependency_links: + warnings.warn( + "Dependency Links processing has been deprecated and will be " + "removed in a future release.", + RemovedInPip9Warning, + ) + self.dependency_links.extend(links) + + @staticmethod + def _sort_locations(locations, expand_dir=False): + """ + Sort locations into "files" (archives) and "urls", and return + a pair of lists (files,urls) + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate list + def sort_path(path): + url = path_to_url(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + + is_local_path = os.path.exists(url) + is_file_url = url.startswith('file:') + + if is_local_path or is_file_url: + if is_local_path: + path = url + else: + path = url_to_path(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) + elif os.path.isfile(path): + sort_path(path) + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url) + elif is_url(url): + # Only add url with clear scheme + urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url) + + return files, urls + + def _candidate_sort_key(self, candidate): + """ + Function used to generate link sort key for link tuples. + The greater the return value, the more preferred it is. + If not finding wheels, then sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min() + 3. source archives + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + support_num = len(supported_tags) + if candidate.location.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(candidate.location.filename) + if not wheel.supported(): + raise UnsupportedWheel( + "%s is not a supported wheel for this platform. It " + "can't be sorted." % wheel.filename + ) + pri = -(wheel.support_index_min()) + else: # sdist + pri = -(support_num) + return (candidate.version, pri) + + def _validate_secure_origin(self, logger, location): + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin = (parsed.scheme, parsed.hostname, parsed.port) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + protocol = origin[0].rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in (SECURE_ORIGINS + self.secure_origins): + if protocol != secure_origin[0] and secure_origin[0] != "*": + continue + + try: + # We need to do this decode dance to ensure that we have a + # unicode object, even on Python 2.x. + addr = ipaddress.ip_address( + origin[1] + if ( + isinstance(origin[1], six.text_type) or + origin[1] is None + ) + else origin[1].decode("utf8") + ) + network = ipaddress.ip_network( + secure_origin[1] + if isinstance(secure_origin[1], six.text_type) + else secure_origin[1].decode("utf8") + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if (origin[1] and + origin[1].lower() != secure_origin[1].lower() and + secure_origin[1] != "*"): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port patches + if (origin[2] != secure_origin[2] and + secure_origin[2] != "*" and + secure_origin[2] is not None): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS it " + "is recommended to use HTTPS instead, otherwise you may silence " + "this warning and allow it anyways with '--trusted-host %s'.", + parsed.hostname, + parsed.hostname, + ) + + return False + + def _get_index_urls_locations(self, project_name): + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url): + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] + + def find_all_candidates(self, project_name): + """Find all available InstallationCandidate for project_name + + This checks index_urls, find_links and dependency_links. + All versions found are returned as an InstallationCandidate list. + + See _link_package_versions for details on which files are accepted + """ + index_locations = self._get_index_urls_locations(project_name) + index_file_loc, index_url_loc = self._sort_locations(index_locations) + fl_file_loc, fl_url_loc = self._sort_locations( + self.find_links, expand_dir=True) + dep_file_loc, dep_url_loc = self._sort_locations(self.dependency_links) + + file_locations = ( + Link(url) for url in itertools.chain( + index_file_loc, fl_file_loc, dep_file_loc) + ) + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-links + # We explicitly do not trust links that came from dependency_links + # We want to filter out any thing which does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + (Link(url) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + (Link(url) for url in dep_url_loc), + ) + if self._validate_secure_origin(logger, link) + ] + + logger.debug('%d location(s) to search for versions of %s:', + len(url_locations), project_name) + + for location in url_locations: + logger.debug('* %s', location) + + canonical_name = canonicalize_name(project_name) + formats = fmt_ctl_formats(self.format_control, canonical_name) + search = Search(project_name, canonical_name, formats) + find_links_versions = self._package_versions( + # We trust every directly linked archive in find_links + (Link(url, '-f') for url in self.find_links), + search + ) + + page_versions = [] + for page in self._get_pages(url_locations, project_name): + logger.debug('Analyzing links from page %s', page.url) + with indent_log(): + page_versions.extend( + self._package_versions(page.links, search) + ) + + dependency_versions = self._package_versions( + (Link(url) for url in self.dependency_links), search + ) + if dependency_versions: + logger.debug( + 'dependency_links found: %s', + ', '.join([ + version.location.url for version in dependency_versions + ]) + ) + + file_versions = self._package_versions(file_locations, search) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.location.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return ( + file_versions + find_links_versions + page_versions + + dependency_versions + ) + + def find_requirement(self, req, upgrade): + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a Link if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + all_candidates = self.find_all_candidates(req.name) + + # Filter out anything which doesn't match our specifier + compatible_versions = set( + req.specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + [str(c.version) for c in all_candidates], + prereleases=( + self.allow_all_prereleases + if self.allow_all_prereleases else None + ), + ) + ) + applicable_candidates = [ + # Again, converting to str to deal with debundling. + c for c in all_candidates if str(c.version) in compatible_versions + ] + + if applicable_candidates: + best_candidate = max(applicable_candidates, + key=self._candidate_sort_key) + else: + best_candidate = None + + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + else: + installed_version = None + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + ', '.join( + sorted( + set(str(c.version) for c in all_candidates), + key=parse_version, + ) + ) + ) + + raise DistributionNotFound( + 'No matching distribution found for %s' % req + ) + + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + ', '.join(sorted(compatible_versions, key=parse_version)) or + "none", + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + ', '.join(sorted(compatible_versions, key=parse_version)) + ) + return best_candidate.location + + def _get_pages(self, locations, project_name): + """ + Yields (page, page_url) from the given locations, skipping + locations that have errors. + """ + seen = set() + for location in locations: + if location in seen: + continue + seen.add(location) + + page = self._get_page(location) + if page is None: + continue + + yield page + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + def _sort_links(self, links): + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _package_versions(self, links, search): + result = [] + for link in self._sort_links(links): + v = self._link_package_versions(link, search) + if v is not None: + result.append(v) + return result + + def _log_skipped_link(self, link, reason): + if link not in self.logged_links: + logger.debug('Skipping link %s; %s', link, reason) + self.logged_links.add(link) + + def _link_package_versions(self, link, search): + """Return an InstallationCandidate or None""" + + version = None + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + self._log_skipped_link(link, 'not a file') + return + if ext not in SUPPORTED_EXTENSIONS: + self._log_skipped_link( + link, 'unsupported archive format: %s' % ext) + return + if "binary" not in search.formats and ext == wheel_ext: + self._log_skipped_link( + link, 'No binaries permitted for %s' % search.supplied) + return + if "macosx10" in link.path and ext == '.zip': + self._log_skipped_link(link, 'macosx10 one') + return + if ext == wheel_ext: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + self._log_skipped_link(link, 'invalid wheel filename') + return + if canonicalize_name(wheel.name) != search.canonical: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return + if not wheel.supported(): + self._log_skipped_link( + link, 'it is not compatible with this Python') + return + + version = wheel.version + + # This should be up by the search.ok_binary check, but see issue 2700. + if "source" not in search.formats and ext != wheel_ext: + self._log_skipped_link( + link, 'No sources permitted for %s' % search.supplied) + return + + if not version: + version = egg_info_matches(egg_info, search.supplied, link) + if version is None: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != sys.version[:3]: + self._log_skipped_link( + link, 'Python version is incorrect') + return + logger.debug('Found link %s, version: %s', link, version) + + return InstallationCandidate(search.supplied, version, link) + + def _get_page(self, link): + return HTMLPage.get_page(link, session=self.session) + + +def egg_info_matches( + egg_info, search_name, link, + _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + """Pull the version part out of a string. + + :param egg_info: The string to parse. E.g. foo-2.1 + :param search_name: The name of the package this belongs to. None to + infer the name. Note that this cannot unambiguously parse strings + like foo-2-2 which might be foo, 2-2 or foo-2, 2. + :param link: The link the string came from, for logging on failure. + """ + match = _egg_info_re.search(egg_info) + if not match: + logger.debug('Could not parse version from link: %s', link) + return None + if search_name is None: + full_match = match.group(0) + return full_match[full_match.index('-'):] + name = match.group(0).lower() + # To match the "safe" name that pkg_resources creates: + name = name.replace('_', '-') + # project name and version must be separated by a dash + look_for = search_name.lower() + "-" + if name.startswith(look_for): + return match.group(0)[len(look_for):] + else: + return None + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__(self, content, url, headers=None): + # Determine if we have any encoding information in our headers + encoding = None + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + + if "charset" in params: + encoding = params['charset'] + + self.content = content + self.parsed = html5lib.parse( + self.content, + encoding=encoding, + namespaceHTMLElements=False, + ) + self.url = url + self.headers = headers + + def __str__(self): + return self.url + + @classmethod + def get_page(cls, link, skip_archives=True, session=None): + if session is None: + raise TypeError( + "get_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url + url = url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + from pip.vcs import VcsSupport + for scheme in VcsSupport.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + logger.debug('Cannot look at %s URL %s', scheme, link) + return None + + try: + if skip_archives: + filename = link.filename + for bad_ext in ARCHIVE_EXTENSIONS: + if filename.endswith(bad_ext): + content_type = cls._get_content_type( + url, session=session, + ) + if content_type.lower().startswith('text/html'): + break + else: + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + logger.debug('Getting page %s', url) + + # Tack index.html onto file:// URLs that point to directories + (scheme, netloc, path, params, query, fragment) = \ + urllib_parse.urlparse(url) + if (scheme == 'file' and + os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith('/'): + url += '/' + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + "Cache-Control": "max-age=600", + }, + ) + resp.raise_for_status() + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. + content_type = resp.headers.get('Content-Type', 'unknown') + if not content_type.lower().startswith("text/html"): + logger.debug( + 'Skipping page %s because of Content-Type: %s', + link, + content_type, + ) + return + + inst = cls(resp.content, resp.url, resp.headers) + except requests.HTTPError as exc: + cls._handle_fail(link, exc, url) + except SSLError as exc: + reason = ("There was a problem confirming the ssl certificate: " + "%s" % exc) + cls._handle_fail(link, reason, url, meth=logger.info) + except requests.ConnectionError as exc: + cls._handle_fail(link, "connection error: %s" % exc, url) + except requests.Timeout: + cls._handle_fail(link, "timed out", url) + else: + return inst + + @staticmethod + def _handle_fail(link, reason, url, meth=None): + if meth is None: + meth = logger.debug + + meth("Could not fetch URL %s: %s - skipping", link, reason) + + @staticmethod + def _get_content_type(url, session): + """Get the Content-Type of the given url, using a HEAD request""" + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in ('http', 'https'): + # FIXME: some warning or something? + # assertion error? + return '' + + resp = session.head(url, allow_redirects=True) + resp.raise_for_status() + + return resp.headers.get("Content-Type", "") + + @cached_property + def base_url(self): + bases = [ + x for x in self.parsed.findall(".//base") + if x.get("href") is not None + ] + if bases and bases[0].get("href"): + return bases[0].get("href") + else: + return self.url + + @property + def links(self): + """Yields all links in the page""" + for anchor in self.parsed.findall(".//a"): + if anchor.get("href"): + href = anchor.get("href") + url = self.clean_link( + urllib_parse.urljoin(self.base_url, href) + ) + yield Link(url, self) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + def clean_link(self, url): + """Makes sure a link is fully encoded. That is, if a ' ' shows up in + the link, it will be rewritten to %20 (while not over-quoting + % or other characters).""" + return self._clean_re.sub( + lambda match: '%%%2x' % ord(match.group(0)), url) + + +class Link(object): + + def __init__(self, url, comes_from=None): + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self.url = url + self.comes_from = comes_from + + def __str__(self): + if self.comes_from: + return '%s (from %s)' % (self.url, self.comes_from) + else: + return str(self.url) + + def __repr__(self): + return '' % self + + def __eq__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url == other.url + + def __ne__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url != other.url + + def __lt__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url < other.url + + def __le__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url <= other.url + + def __gt__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url > other.url + + def __ge__(self, other): + if not isinstance(other, Link): + return NotImplemented + return self.url >= other.url + + def __hash__(self): + return hash(self.url) + + @property + def filename(self): + _, netloc, path, _, _ = urllib_parse.urlsplit(self.url) + name = posixpath.basename(path.rstrip('/')) or netloc + name = urllib_parse.unquote(name) + assert name, ('URL %r produced no filename' % self.url) + return name + + @property + def scheme(self): + return urllib_parse.urlsplit(self.url)[0] + + @property + def netloc(self): + return urllib_parse.urlsplit(self.url)[1] + + @property + def path(self): + return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2]) + + def splitext(self): + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + return self.splitext()[1] + + @property + def url_without_fragment(self): + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url) + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + + @property + def egg_fragment(self): + match = self._egg_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + match = self._subdirectory_fragment_re.search(self.url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) + + @property + def hash(self): + match = self._hash_re.search(self.url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + match = self._hash_re.search(self.url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_wheel(self): + return self.ext == wheel_ext + + @property + def is_artifact(self): + """ + Determines if this points to an actual artifact (e.g. a tarball) or if + it points to an "abstract" thing like a path or a VCS location. + """ + from pip.vcs import vcs + + if self.scheme in vcs.all_schemes: + return False + + return True + + +FormatControl = namedtuple('FormatControl', 'no_binary only_binary') +"""This object has two fields, no_binary and only_binary. + +If a field is falsy, it isn't set. If it is {':all:'}, it should match all +packages except those listed in the other field. Only one field can be set +to {':all:'} at a time. The rest of the time exact package name matches +are listed, with any given package only showing up in one field at a time. +""" + + +def fmt_ctl_handle_mutual_exclude(value, target, other): + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + if ':none:' not in new: + # Without a none, we want to discard everything as :all: covers it + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + +def fmt_ctl_formats(fmt_ctl, canonical_name): + result = set(["binary", "source"]) + if canonical_name in fmt_ctl.only_binary: + result.discard('source') + elif canonical_name in fmt_ctl.no_binary: + result.discard('binary') + elif ':all:' in fmt_ctl.only_binary: + result.discard('source') + elif ':all:' in fmt_ctl.no_binary: + result.discard('binary') + return frozenset(result) + + +def fmt_ctl_no_binary(fmt_ctl): + fmt_ctl_handle_mutual_exclude( + ':all:', fmt_ctl.no_binary, fmt_ctl.only_binary) + + +def fmt_ctl_no_use_wheel(fmt_ctl): + fmt_ctl_no_binary(fmt_ctl) + warnings.warn( + '--no-use-wheel is deprecated and will be removed in the future. ' + ' Please use --no-binary :all: instead.', RemovedInPip10Warning, + stacklevel=2) + + +Search = namedtuple('Search', 'supplied canonical formats') +"""Capture key aspects of a search. + +:attribute supplied: The user supplied package. +:attribute canonical: The canonical package name. +:attribute formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. +""" diff --git a/venv/lib/python3.5/site-packages/pip/locations.py b/venv/lib/python3.5/site-packages/pip/locations.py new file mode 100644 index 0000000..1bd0fae --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/locations.py @@ -0,0 +1,182 @@ +"""Locations where we look for configs, install stuff, etc""" +from __future__ import absolute_import + +import os +import os.path +import site +import sys + +from distutils import sysconfig +from distutils.command.install import install, SCHEME_KEYS # noqa + +from pip.compat import WINDOWS, expanduser +from pip.utils import appdirs + + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +DELETE_MARKER_MESSAGE = '''\ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). +''' +PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt' + + +def write_delete_marker_file(directory): + """ + Write the pip delete marker file into this directory. + """ + filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME) + with open(filepath, 'w') as marker_fp: + marker_fp.write(DELETE_MARKER_MESSAGE) + + +def running_under_virtualenv(): + """ + Return True if we're running inside a virtualenv, False otherwise. + + """ + if hasattr(sys, 'real_prefix'): + return True + elif sys.prefix != getattr(sys, "base_prefix", sys.prefix): + return True + + return False + + +def virtualenv_no_global(): + """ + Return True if in a venv and no system site packages. + """ + # this mirrors the logic in virtualenv.py for locating the + # no-global-site-packages.txt file + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt') + if running_under_virtualenv() and os.path.isfile(no_global_file): + return True + + +if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, 'src') +else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), 'src') + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit( + "The folder you are executing pip from can no longer be found." + ) + +# under Mac OS X + virtualenv sys.prefix is not properly resolved +# it is something like /path/to/python/bin/.. +# Note: using realpath due to tmp dirs on OSX being symlinks +src_prefix = os.path.abspath(src_prefix) + +# FIXME doesn't account for venv linked to global site-packages + +site_packages = sysconfig.get_python_lib() +user_site = site.USER_SITE +user_dir = expanduser('~') +if WINDOWS: + bin_py = os.path.join(sys.prefix, 'Scripts') + bin_user = os.path.join(user_site, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.ini' + + legacy_storage_dir = os.path.join(user_dir, 'pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + config_basename = 'pip.conf' + + legacy_storage_dir = os.path.join(user_dir, '.pip') + legacy_config_file = os.path.join( + legacy_storage_dir, + config_basename, + ) + + # Forcing to use /usr/local/bin for standard Mac OS X framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' + +site_config_files = [ + os.path.join(path, config_basename) + for path in appdirs.site_config_dirs('pip') +] + + +def distutils_scheme(dist_name, user=False, home=None, root=None, + isolated=False, prefix=None): + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + scheme = {} + + if isolated: + extra_dist_args = {"script_args": ["--no-user-cfg"]} + else: + extra_dist_args = {} + dist_args = {'name': dist_name} + dist_args.update(extra_dist_args) + + d = Distribution(dist_args) + d.parse_config_files() + i = d.get_command_obj('install', create=True) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={0} prefix={1}".format(user, prefix) + i.user = user or i.user + if user: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + sys.prefix, + 'include', + 'site', + 'python' + sys.version[:3], + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join( + root, + path_no_drive[1:], + ) + + return scheme diff --git a/venv/lib/python3.5/site-packages/pip/models/__init__.py b/venv/lib/python3.5/site-packages/pip/models/__init__.py new file mode 100644 index 0000000..1d727d7 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/models/__init__.py @@ -0,0 +1,4 @@ +from pip.models.index import Index, PyPI + + +__all__ = ["Index", "PyPI"] diff --git a/venv/lib/python3.5/site-packages/pip/models/index.py b/venv/lib/python3.5/site-packages/pip/models/index.py new file mode 100644 index 0000000..be99119 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/models/index.py @@ -0,0 +1,16 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class Index(object): + def __init__(self, url): + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self.url_to_path('simple') + self.pypi_url = self.url_to_path('pypi') + self.pip_json_url = self.url_to_path('pypi/pip/json') + + def url_to_path(self, path): + return urllib_parse.urljoin(self.url, path) + + +PyPI = Index('https://pypi.python.org/') diff --git a/venv/lib/python3.5/site-packages/pip/operations/__init__.py b/venv/lib/python3.5/site-packages/pip/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.5/site-packages/pip/operations/freeze.py b/venv/lib/python3.5/site-packages/pip/operations/freeze.py new file mode 100644 index 0000000..7493ced --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/operations/freeze.py @@ -0,0 +1,122 @@ +from __future__ import absolute_import + +import logging +import re + +import pip +from pip.req import InstallRequirement +from pip.utils import get_installed_distributions +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, + find_links=None, local_only=None, user_only=None, skip_regex=None, + default_vcs=None, + isolated=False, + wheel_cache=None, + skip=()): + find_links = find_links or [] + skip_match = None + + if skip_regex: + skip_match = re.compile(skip_regex).search + + dependency_links = [] + + for dist in pkg_resources.working_set: + if dist.has_metadata('dependency_links.txt'): + dependency_links.extend( + dist.get_metadata_lines('dependency_links.txt') + ) + for link in find_links: + if '#egg=' in link: + dependency_links.append(link) + for link in find_links: + yield '-f %s' % link + installations = {} + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only): + try: + req = pip.FrozenRequirement.from_dist( + dist, + dependency_links + ) + except RequirementParseError: + logger.warning( + "Could not parse requirement: %s", + dist.project_name + ) + continue + installations[req.name] = req + + if requirement: + with open(requirement) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + (skip_match and skip_match(line)) or + line.startswith(( + '-r', '--requirement', + '-Z', '--always-unzip', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url'))): + yield line.rstrip() + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = InstallRequirement.from_editable( + line, + default_vcs=default_vcs, + isolated=isolated, + wheel_cache=wheel_cache, + ) + else: + line_req = InstallRequirement.from_line( + line, + isolated=isolated, + wheel_cache=wheel_cache, + ) + + if not line_req.name: + logger.info( + "Skipping line because it's not clear what it " + "would install: %s", + line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + elif line_req.name not in installations: + logger.warning( + "Requirement file contains %s, but that package is" + " not installed", + line.strip(), + ) + else: + yield str(installations[line_req.name]).rstrip() + del installations[line_req.name] + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if canonicalize_name(installation.name) not in skip: + yield str(installation).rstrip() diff --git a/venv/lib/python3.5/site-packages/pip/pep425tags.py b/venv/lib/python3.5/site-packages/pip/pep425tags.py new file mode 100644 index 0000000..e118457 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/pep425tags.py @@ -0,0 +1,338 @@ +"""Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import + +import re +import sys +import warnings +import platform +import logging +import ctypes + +try: + import sysconfig +except ImportError: # pragma nocover + # Python < 2.7 + import distutils.sysconfig as sysconfig +import distutils.util + +from pip.compat import OrderedDict + + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def get_config_var(var): + try: + return sysconfig.get_config_var(var) + except IOError as e: # Issue #1074 + warnings.warn("{0}".format(e), RuntimeWarning) + return None + + +def get_abbr_impl(): + """Return abbreviated implementation name.""" + if hasattr(sys, 'pypy_version_info'): + pyimpl = 'pp' + elif sys.platform.startswith('java'): + pyimpl = 'jy' + elif sys.platform == 'cli': + pyimpl = 'ip' + else: + pyimpl = 'cp' + return pyimpl + + +def get_impl_ver(): + """Return implementation version.""" + impl_ver = get_config_var("py_version_nodot") + if not impl_ver or get_abbr_impl() == 'pp': + impl_ver = ''.join(map(str, get_impl_version_info())) + return impl_ver + + +def get_impl_version_info(): + """Return sys.version_info-like tuple for use in decrementing the minor + version.""" + if get_abbr_impl() == 'pp': + # as per https://github.com/pypa/pip/issues/2882 + return (sys.version_info[0], sys.pypy_version_info.major, + sys.pypy_version_info.minor) + else: + return sys.version_info[0], sys.version_info[1] + + +def get_impl_tag(): + """ + Returns the Tag for this specific implementation. + """ + return "{0}{1}".format(get_abbr_impl(), get_impl_ver()) + + +def get_flag(var, fallback, expected=True, warn=True): + """Use a fallback method for determining SOABI flags if the needed config + var is unset or unavailable.""" + val = get_config_var(var) + if val is None: + if warn: + logger.debug("Config variable '%s' is unset, Python ABI tag may " + "be incorrect", var) + return fallback() + return val == expected + + +def get_abi_tag(): + """Return the ABI tag based on SOABI (if available) or emulate SOABI + (CPython 2, PyPy).""" + soabi = get_config_var('SOABI') + impl = get_abbr_impl() + if not soabi and impl in ('cp', 'pp') and hasattr(sys, 'maxunicode'): + d = '' + m = '' + u = '' + if get_flag('Py_DEBUG', + lambda: hasattr(sys, 'gettotalrefcount'), + warn=(impl == 'cp')): + d = 'd' + if get_flag('WITH_PYMALLOC', + lambda: impl == 'cp', + warn=(impl == 'cp')): + m = 'm' + if get_flag('Py_UNICODE_SIZE', + lambda: sys.maxunicode == 0x10ffff, + expected=4, + warn=(impl == 'cp' and + sys.version_info < (3, 3))) \ + and sys.version_info < (3, 3): + u = 'u' + abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) + elif soabi and soabi.startswith('cpython-'): + abi = 'cp' + soabi.split('-')[1] + elif soabi: + abi = soabi.replace('.', '_').replace('-', '_') + else: + abi = None + return abi + + +def _is_running_32bit(): + return sys.maxsize == 2147483647 + + +def get_platform(): + """Return our platform name 'win32', 'linux_x86_64'""" + if sys.platform == 'darwin': + # distutils.util.get_platform() returns the release based on the value + # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may + # be signficantly older than the user's current machine. + release, _, machine = platform.mac_ver() + split_ver = release.split('.') + + if machine == "x86_64" and _is_running_32bit(): + machine = "i386" + elif machine == "ppc64" and _is_running_32bit(): + machine = "ppc" + + return 'macosx_{0}_{1}_{2}'.format(split_ver[0], split_ver[1], machine) + + # XXX remove distutils dependency + result = distutils.util.get_platform().replace('.', '_').replace('-', '_') + if result == "linux_x86_64" and _is_running_32bit(): + # 32 bit Python program (running on a 64 bit Linux): pip should only + # install and run 32 bit compiled extensions in that case. + result = "linux_i686" + + return result + + +def is_manylinux1_compatible(): + # Only Linux, and only x86-64 / i686 + if get_platform() not in ("linux_x86_64", "linux_i686"): + return False + + # Check for presence of _manylinux module + try: + import _manylinux + return bool(_manylinux.manylinux1_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 5 uses glibc 2.5. + return have_compatible_glibc(2, 5) + + +def have_compatible_glibc(major, minimum_minor): + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return False + + # Call gnu_get_libc_version, which returns a string like "2.5". + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + # Parse string and check against requested version. + version = [int(piece) for piece in version_str.split(".")] + if len(version) < 2: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return version[0] == major and version[1] >= minimum_minor + + +def get_darwin_arches(major, minor, machine): + """Return a list of supported arches (including group arches) for + the given major, minor and machine architecture of an OS X machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # Looking at the application support for OS X versions in the chart + # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears + # our timeline looks roughly like: + # + # 10.0 - Introduces ppc support. + # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64 + # and x86_64 support is CLI only, and cannot be used for GUI + # applications. + # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications. + # 10.6 - Drops support for ppc64 + # 10.7 - Drops support for ppc + # + # Given that we do not know if we're installing a CLI or a GUI + # application, we must be conservative and assume it might be a GUI + # application and behave as if ppc64 and x86_64 support did not occur + # until 10.5. + # + # Note: The above information is taken from the "Application support" + # column in the chart not the "Processor support" since I believe + # that we care about what instruction sets an application can use + # not which processors the OS supports. + if arch == 'ppc': + return (major, minor) <= (10, 5) + if arch == 'ppc64': + return (major, minor) == (10, 5) + if arch == 'i386': + return (major, minor) >= (10, 4) + if arch == 'x86_64': + return (major, minor) >= (10, 5) + if arch in groups: + for garch in groups[arch]: + if _supports_arch(major, minor, garch): + return True + return False + + groups = OrderedDict([ + ("fat", ("i386", "ppc")), + ("intel", ("x86_64", "i386")), + ("fat64", ("x86_64", "ppc64")), + ("fat32", ("x86_64", "i386", "ppc")), + ]) + + if _supports_arch(major, minor, machine): + arches.append(machine) + + for garch in groups: + if machine in groups[garch] and _supports_arch(major, minor, garch): + arches.append(garch) + + arches.append('universal') + + return arches + + +def get_supported(versions=None, noarch=False): + """Return a list of supported tags for each version specified in + `versions`. + + :param versions: a list of string versions, of the form ["33", "32"], + or None. The first version will be assumed to support our ABI. + """ + supported = [] + + # Versions must be given with respect to the preference + if versions is None: + versions = [] + version_info = get_impl_version_info() + major = version_info[:-1] + # Support all previous minor Python versions. + for minor in range(version_info[-1], -1, -1): + versions.append(''.join(map(str, major + (minor,)))) + + impl = get_abbr_impl() + + abis = [] + + abi = get_abi_tag() + if abi: + abis[0:0] = [abi] + + abi3s = set() + import imp + for suffix in imp.get_suffixes(): + if suffix[0].startswith('.abi'): + abi3s.add(suffix[0].split('.', 2)[1]) + + abis.extend(sorted(list(abi3s))) + + abis.append('none') + + if not noarch: + arch = get_platform() + if sys.platform == 'darwin': + # support macosx-10.6-intel on macosx-10.9-x86_64 + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + tpl = '{0}_{1}_%i_%s'.format(name, major) + arches = [] + for m in reversed(range(int(minor) + 1)): + for a in get_darwin_arches(int(major), m, actual_arch): + arches.append(tpl % (m, a)) + else: + # arch pattern didn't match (?!) + arches = [arch] + elif is_manylinux1_compatible(): + arches = [arch.replace('linux', 'manylinux1'), arch] + else: + arches = [arch] + + # Current version, current API (built specifically for our Python): + for abi in abis: + for arch in arches: + supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + + # Has binaries, does not use the Python API: + for arch in arches: + supported.append(('py%s' % (versions[0][0]), 'none', arch)) + + # No abi / arch, but requires our implementation: + supported.append(('%s%s' % (impl, versions[0]), 'none', 'any')) + # Tagged specifically as being cross-version compatible + # (with just the major version specified) + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + + # No abi / arch, generic Python + for i, version in enumerate(versions): + supported.append(('py%s' % (version,), 'none', 'any')) + if i == 0: + supported.append(('py%s' % (version[0]), 'none', 'any')) + + return supported + +supported_tags = get_supported() +supported_tags_noarch = get_supported(noarch=True) + +implementation_tag = get_impl_tag() diff --git a/venv/lib/python3.5/site-packages/pip/req/__init__.py b/venv/lib/python3.5/site-packages/pip/req/__init__.py new file mode 100644 index 0000000..00185a4 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/req/__init__.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from .req_install import InstallRequirement +from .req_set import RequirementSet, Requirements +from .req_file import parse_requirements + +__all__ = [ + "RequirementSet", "Requirements", "InstallRequirement", + "parse_requirements", +] diff --git a/venv/lib/python3.5/site-packages/pip/req/req_file.py b/venv/lib/python3.5/site-packages/pip/req/req_file.py new file mode 100644 index 0000000..2cfb479 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/req/req_file.py @@ -0,0 +1,342 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import os +import re +import shlex +import sys +import optparse +import warnings + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves import filterfalse + +import pip +from pip.download import get_file_content +from pip.req.req_install import InstallRequirement +from pip.exceptions import (RequirementsFileParseError) +from pip.utils.deprecation import RemovedInPip10Warning +from pip import cmdoptions + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s)+#.*$') + +SUPPORTED_OPTIONS = [ + cmdoptions.constraints, + cmdoptions.editable, + cmdoptions.requirements, + cmdoptions.no_index, + cmdoptions.index_url, + cmdoptions.find_links, + cmdoptions.extra_index_url, + cmdoptions.allow_external, + cmdoptions.allow_all_external, + cmdoptions.no_allow_external, + cmdoptions.allow_unsafe, + cmdoptions.no_allow_unsafe, + cmdoptions.use_wheel, + cmdoptions.no_use_wheel, + cmdoptions.always_unzip, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.pre, + cmdoptions.process_dependency_links, + cmdoptions.trusted_host, + cmdoptions.require_hashes, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [o().dest for o in SUPPORTED_OPTIONS_REQ] + + +def parse_requirements(filename, finder=None, comes_from=None, options=None, + session=None, constraint=False, wheel_cache=None): + """Parse a requirements file and yield InstallRequirement instances. + + :param filename: Path or url of requirements file. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param session: Instance of pip.download.PipSession. + :param constraint: If true, parsing a constraint file rather than + requirements file. + :param wheel_cache: Instance of pip.wheel.WheelCache + """ + if session is None: + raise TypeError( + "parse_requirements() missing 1 required keyword argument: " + "'session'" + ) + + _, content = get_file_content( + filename, comes_from=comes_from, session=session + ) + + lines_enum = preprocess(content, options) + + for line_number, line in lines_enum: + req_iter = process_line(line, filename, line_number, finder, + comes_from, options, session, wheel_cache, + constraint=constraint) + for req in req_iter: + yield req + + +def preprocess(content, options): + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + :param options: cli options + """ + lines_enum = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = skip_regex(lines_enum, options) + return lines_enum + + +def process_line(line, filename, line_number, finder=None, comes_from=None, + options=None, session=None, wheel_cache=None, + constraint=False): + """Process a single requirements line; This can result in creating/yielding + requirements, or updating the finder. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + + :param constraint: If True, parsing a constraints file. + :param options: OptionParser options that we may update + """ + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + # `finder.format_control` will be updated during parsing + defaults.format_control = finder.format_control + args_str, options_str = break_args_options(line) + if sys.version_info < (2, 7, 3): + # Priori to 2.7.3, shlex can not deal with unicode entries + options_str = options_str.encode('utf8') + opts, _ = parser.parse_args(shlex.split(options_str), defaults) + + # preserve for the nested code path + line_comes_from = '%s %s (line %s)' % ( + '-c' if constraint else '-r', filename, line_number) + + # yield a line requirement + if args_str: + isolated = options.isolated_mode if options else False + if options: + cmdoptions.check_install_build_global(options, opts) + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in opts.__dict__ and opts.__dict__[dest]: + req_options[dest] = opts.__dict__[dest] + yield InstallRequirement.from_line( + args_str, line_comes_from, constraint=constraint, + isolated=isolated, options=req_options, wheel_cache=wheel_cache + ) + + # yield an editable requirement + elif opts.editables: + isolated = options.isolated_mode if options else False + default_vcs = options.default_vcs if options else None + yield InstallRequirement.from_editable( + opts.editables[0], comes_from=line_comes_from, + constraint=constraint, default_vcs=default_vcs, isolated=isolated, + wheel_cache=wheel_cache + ) + + # parse a nested requirements file + elif opts.requirements or opts.constraints: + if opts.requirements: + req_path = opts.requirements[0] + nested_constraint = False + else: + req_path = opts.constraints[0] + nested_constraint = True + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join(os.path.dirname(filename), req_path) + # TODO: Why not use `comes_from='-r {} (line {})'` here as well? + parser = parse_requirements( + req_path, finder, comes_from, options, session, + constraint=nested_constraint, wheel_cache=wheel_cache + ) + for req in parser: + yield req + + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + if opts.allow_external: + warnings.warn( + "--allow-external has been deprecated and will be removed in " + "the future. Due to changes in the repository protocol, it no " + "longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.allow_all_external: + warnings.warn( + "--allow-all-external has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.allow_unverified: + warnings.warn( + "--allow-unverified has been deprecated and will be removed " + "in the future. Due to changes in the repository protocol, it " + "no longer has any effect.", + RemovedInPip10Warning, + ) + + if opts.index_url: + finder.index_urls = [opts.index_url] + if opts.use_wheel is False: + finder.use_wheel = False + pip.index.fmt_ctl_no_use_wheel(finder.format_control) + if opts.no_index is True: + finder.index_urls = [] + if opts.extra_index_urls: + finder.index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + finder.find_links.append(value) + if opts.pre: + finder.allow_all_prereleases = True + if opts.process_dependency_links: + finder.process_dependency_links = True + if opts.trusted_hosts: + finder.secure_origins.extend( + ("*", host, "*") for host in opts.trusted_hosts) + + +def break_args_options(line): + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) + + +def build_parser(): + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + raise RequirementsFileParseError(msg) + parser.exit = parser_exit + + return parser + + +def join_lines(lines_enum): + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def skip_regex(lines_enum, options): + """ + Skip lines that match '--skip-requirements-regex' pattern + + Note: the regex pattern is only built once + """ + skip_regex = options.skip_requirements_regex if options else None + if skip_regex: + pattern = re.compile(skip_regex) + lines_enum = filterfalse( + lambda e: pattern.search(e[1]), + lines_enum) + return lines_enum diff --git a/venv/lib/python3.5/site-packages/pip/req/req_install.py b/venv/lib/python3.5/site-packages/pip/req/req_install.py new file mode 100644 index 0000000..922173f --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/req/req_install.py @@ -0,0 +1,1190 @@ +from __future__ import absolute_import + +import logging +import os +import re +import shutil +import sys +import tempfile +import traceback +import warnings +import zipfile + +from distutils import sysconfig +from distutils.util import change_root +from email.parser import FeedParser + +from pip._vendor import pkg_resources, six +from pip._vendor.distlib.markers import interpret as markers_interpret +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.six.moves import configparser + +import pip.wheel + +from pip.compat import native_str, get_stdlib, WINDOWS +from pip.download import is_url, url_to_path, path_to_url, is_archive_file +from pip.exceptions import ( + InstallationError, UninstallationError, UnsupportedWheel, +) +from pip.locations import ( + bin_py, running_under_virtualenv, PIP_DELETE_MARKER_FILENAME, bin_user, +) +from pip.utils import ( + display_path, rmtree, ask_path_exists, backup_dir, is_installable_dir, + dist_in_usersite, dist_in_site_packages, egg_link_path, + call_subprocess, read_text_file, FakeFile, _make_build_dir, ensure_dir, + get_installed_version, normalize_path, dist_is_local, +) + +from pip.utils.hashes import Hashes +from pip.utils.deprecation import RemovedInPip9Warning, RemovedInPip10Warning +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip.utils.ui import open_spinner +from pip.req.req_uninstall import UninstallPathSet +from pip.vcs import vcs +from pip.wheel import move_wheel_files, Wheel + + +logger = logging.getLogger(__name__) + +operators = specifiers.Specifier._operators.keys() + + +def _strip_extras(path): + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +class InstallRequirement(object): + + def __init__(self, req, comes_from, source_dir=None, editable=False, + link=None, as_egg=False, update=True, + pycompile=True, markers=None, isolated=False, options=None, + wheel_cache=None, constraint=False): + self.extras = () + if isinstance(req, six.string_types): + try: + req = Requirement(req) + except InvalidRequirement: + if os.path.sep in req: + add_msg = "It looks like a path. Does it exist ?" + elif '=' in req and not any(op in req for op in operators): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = traceback.format_exc() + raise InstallationError( + "Invalid requirement: '%s'\n%s" % (req, add_msg)) + self.extras = req.extras + + self.req = req + self.comes_from = comes_from + self.constraint = constraint + self.source_dir = source_dir + self.editable = editable + + self._wheel_cache = wheel_cache + self.link = self.original_link = link + self.as_egg = as_egg + self.markers = markers + self._egg_info_path = None + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None + # This hold the pkg_resources.Distribution object if this requirement + # conflicts with another installed distribution: + self.conflicts_with = None + # Temporary build location + self._temp_build_dir = None + # Used to store the global directory where the _temp_build_dir should + # have been created. Cf _correct_build_location method. + self._ideal_build_dir = None + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + self.install_succeeded = None + # UninstallPathSet of uninstalled distribution (for possible rollback) + self.uninstalled = None + # Set True if a legitimate do-nothing-on-uninstall has happened - e.g. + # system site packages, stdlib packages. + self.nothing_to_uninstall = False + self.use_user_site = False + self.target_dir = None + self.options = options if options else {} + self.pycompile = pycompile + # Set to True after successful preparation of this requirement + self.prepared = False + + self.isolated = isolated + + @classmethod + def from_editable(cls, editable_req, comes_from=None, default_vcs=None, + isolated=False, options=None, wheel_cache=None, + constraint=False): + from pip.index import Link + + name, url, extras_override = parse_editable( + editable_req, default_vcs) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + + res = cls(name, comes_from, source_dir=source_dir, + editable=True, + link=Link(url), + constraint=constraint, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache) + + if extras_override is not None: + res.extras = extras_override + + return res + + @classmethod + def from_line( + cls, name, comes_from=None, isolated=False, options=None, + wheel_cache=None, constraint=False): + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + from pip.index import Link + + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers = name.split(marker_sep, 1) + markers = markers.strip() + if not markers: + markers = None + else: + markers = None + name = name.strip() + req = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras = None + + if is_url(name): + link = Link(name) + else: + p, extras = _strip_extras(path) + if (os.path.isdir(p) and + (os.path.sep in name or name.startswith('.'))): + + if not is_installable_dir(p): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' " + "not found." % name + ) + link = Link(path_to_url(p)) + elif is_archive_file(p): + if not os.path.isfile(p): + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + link = Link(path_to_url(p)) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + if not wheel.supported(): + raise UnsupportedWheel( + "%s is not a supported wheel on this platform." % + wheel.filename + ) + req = "%s==%s" % (wheel.name, wheel.version) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req = link.egg_fragment + + # a requirement specifier + else: + req = name + + options = options if options else {} + res = cls(req, comes_from, link=link, markers=markers, + isolated=isolated, options=options, + wheel_cache=wheel_cache, constraint=constraint) + + if extras: + res.extras = Requirement('placeholder' + extras).extras + + return res + + def __str__(self): + if self.req: + s = str(self.req) + if self.link: + s += ' from %s' % self.link.url + else: + s = self.link.url if self.link else None + if self.satisfied_by is not None: + s += ' in %s' % display_path(self.satisfied_by.location) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from %s)' % comes_from + return s + + def __repr__(self): + return '<%s object: %s editable=%r>' % ( + self.__class__.__name__, str(self), self.editable) + + def populate_link(self, finder, upgrade, require_hashes): + """Ensure that if a link can be found for this, that it is found. + + Note that self.link may still be None - if Upgrade is False and the + requirement is already installed. + + If require_hashes is True, don't use the wheel cache, because cached + wheels, always built locally, have different hashes than the files + downloaded from the index server and thus throw false hash mismatches. + Furthermore, cached wheels at present have undeterministic contents due + to file modification times. + """ + if self.link is None: + self.link = finder.find_requirement(self, upgrade) + if self._wheel_cache is not None and not require_hashes: + old_link = self.link + self.link = self._wheel_cache.cached_wheel(self.link, self.name) + if old_link != self.link: + logger.debug('Using cached wheel link: %s', self.link) + + @property + def specifier(self): + return self.req.specifier + + @property + def is_pinned(self): + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in ('==', '===')) + + def from_path(self): + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def build_location(self, build_dir): + if self._temp_build_dir is not None: + return self._temp_build_dir + if self.req is None: + # for requirement via a path to a directory: the name of the + # package is not available yet so we create a temp directory + # Once run_egg_info will have run, we'll be able + # to fix it via _correct_build_location + self._temp_build_dir = tempfile.mkdtemp('-build', 'pip-') + self._ideal_build_dir = build_dir + return self._temp_build_dir + if self.editable: + name = self.name.lower() + else: + name = self.name + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + _make_build_dir(build_dir) + return os.path.join(build_dir, name) + + def _correct_build_location(self): + """Move self._temp_build_dir to self._ideal_build_dir/self.req.name + + For some requirements (e.g. a path to a directory), the name of the + package is not available until we run egg_info, so the build_location + will return a temporary directory and store the _ideal_build_dir. + + This is only called by self.egg_info_path to fix the temporary build + directory. + """ + if self.source_dir is not None: + return + assert self.req is not None + assert self._temp_build_dir + assert self._ideal_build_dir + old_location = self._temp_build_dir + self._temp_build_dir = None + new_location = self.build_location(self._ideal_build_dir) + if os.path.exists(new_location): + raise InstallationError( + 'A package already exists in %s; please remove it to continue' + % display_path(new_location)) + logger.debug( + 'Moving package %s from %s to new location %s', + self, display_path(old_location), display_path(new_location), + ) + shutil.move(old_location, new_location) + self._temp_build_dir = new_location + self._ideal_build_dir = None + self.source_dir = new_location + self._egg_info_path = None + + @property + def name(self): + if self.req is None: + return None + return native_str(pkg_resources.safe_name(self.req.name)) + + @property + def setup_py_dir(self): + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py(self): + assert self.source_dir, "No source dir for %s" % self + try: + import setuptools # noqa + except ImportError: + if get_installed_version('setuptools') is None: + add_msg = "Please install setuptools." + else: + add_msg = traceback.format_exc() + # Setuptools is not available + raise InstallationError( + "Could not import setuptools which is required to " + "install from a source distribution.\n%s" % add_msg + ) + + setup_py = os.path.join(self.setup_py_dir, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + def run_egg_info(self): + assert self.source_dir + if self.name: + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + self.setup_py, self.name, + ) + else: + logger.debug( + 'Running setup.py (path:%s) egg_info for package from %s', + self.setup_py, self.link, + ) + + with indent_log(): + script = SETUPTOOLS_SHIM % self.setup_py + base_cmd = [sys.executable, '-c', script] + if self.isolated: + base_cmd += ["--no-user-cfg"] + egg_info_cmd = base_cmd + ['egg_info'] + # We can't put the .egg-info files at the root, because then the + # source code will be mistaken for an installed egg, causing + # problems + if self.editable: + egg_base_option = [] + else: + egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info') + ensure_dir(egg_info_dir) + egg_base_option = ['--egg-base', 'pip-egg-info'] + call_subprocess( + egg_info_cmd + egg_base_option, + cwd=self.setup_py_dir, + show_stdout=False, + command_level=logging.DEBUG, + command_desc='python setup.py egg_info') + + if not self.req: + if isinstance( + pkg_resources.parse_version(self.pkg_info()["Version"]), + Version): + op = "==" + else: + op = "===" + self.req = Requirement( + "".join([ + self.pkg_info()["Name"], + op, + self.pkg_info()["Version"], + ]) + ) + self._correct_build_location() + else: + metadata_name = canonicalize_name(self.pkg_info()["Name"]) + if canonicalize_name(self.req.name) != metadata_name: + logger.warning( + 'Running setup.py (path:%s) egg_info for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.setup_py, self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + def egg_info_data(self, filename): + if self.satisfied_by is not None: + if not self.satisfied_by.has_metadata(filename): + return None + return self.satisfied_by.get_metadata(filename) + assert self.source_dir + filename = self.egg_info_path(filename) + if not os.path.exists(filename): + return None + data = read_text_file(filename) + return data + + def egg_info_path(self, filename): + if self._egg_info_path is None: + if self.editable: + base = self.source_dir + else: + base = os.path.join(self.setup_py_dir, 'pip-egg-info') + filenames = os.listdir(base) + if self.editable: + filenames = [] + for root, dirs, files in os.walk(base): + for dir in vcs.dirnames: + if dir in dirs: + dirs.remove(dir) + # Iterate over a copy of ``dirs``, since mutating + # a list while iterating over it can cause trouble. + # (See https://github.com/pypa/pip/pull/462.) + for dir in list(dirs): + # Don't search in anything that looks like a virtualenv + # environment + if ( + os.path.exists( + os.path.join(root, dir, 'bin', 'python') + ) or + os.path.exists( + os.path.join( + root, dir, 'Scripts', 'Python.exe' + ) + )): + dirs.remove(dir) + # Also don't search through tests + elif dir == 'test' or dir == 'tests': + dirs.remove(dir) + filenames.extend([os.path.join(root, dir) + for dir in dirs]) + filenames = [f for f in filenames if f.endswith('.egg-info')] + + if not filenames: + raise InstallationError( + 'No files/directories in %s (from %s)' % (base, filename) + ) + assert filenames, \ + "No files/directories in %s (from %s)" % (base, filename) + + # if we have more than one match, we pick the toplevel one. This + # can easily be the case if there is a dist folder which contains + # an extracted tarball for testing purposes. + if len(filenames) > 1: + filenames.sort( + key=lambda x: x.count(os.path.sep) + + (os.path.altsep and x.count(os.path.altsep) or 0) + ) + self._egg_info_path = os.path.join(base, filenames[0]) + return os.path.join(self._egg_info_path, filename) + + def pkg_info(self): + p = FeedParser() + data = self.egg_info_data('PKG-INFO') + if not data: + logger.warning( + 'No PKG-INFO file found in %s', + display_path(self.egg_info_path('PKG-INFO')), + ) + p.feed(data or '') + return p.close() + + _requirements_section_re = re.compile(r'\[(.*?)\]') + + @property + def installed_version(self): + return get_installed_version(self.name) + + def assert_source_matches_version(self): + assert self.source_dir + version = self.pkg_info()['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + self.installed_version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + def update_editable(self, obtain=True): + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, "bad url: %r" % self.link.url + if not self.update: + return + vc_type, url = self.link.url.split('+', 1) + backend = vcs.get_backend(vc_type) + if backend: + vcs_backend = backend(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir) + else: + vcs_backend.export(self.source_dir) + else: + assert 0, ( + 'Unexpected version control type (in %s): %s' + % (self.link, vc_type)) + + def uninstall(self, auto_confirm=False): + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + if not self.check_if_exists(): + raise UninstallationError( + "Cannot uninstall requirement %s, not installed" % (self.name,) + ) + dist = self.satisfied_by or self.conflicts_with + + dist_path = normalize_path(dist.location) + if not dist_is_local(dist): + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.key, + dist_path, + sys.prefix, + ) + self.nothing_to_uninstall = True + return + + if dist_path in get_stdlib(): + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + self.nothing_to_uninstall = True + return + + paths_to_remove = UninstallPathSet(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{0}.egg-info'.format( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + warnings.warn( + "Uninstalling a distutils installed project ({0}) has been " + "deprecated and will be removed in a future version. This is " + "due to the fact that uninstalling a distutils project will " + "only partially uninstall the project.".format(self.name), + RemovedInPip10Warning, + ) + paths_to_remove.add(distutils_egg_info) + + elif dist.location.endswith('.egg'): + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link %s does not match installed location of %s ' + '(at %s)' % (link_pointer, self.name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in pip.wheel.uninstallation_paths(dist): + paths_to_remove.add(path) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + if dist.has_metadata('entry_points.txt'): + if six.PY2: + options = {} + else: + options = {"delimiters": ('=', )} + config = configparser.SafeConfigParser(**options) + config.readfp( + FakeFile(dist.get_metadata_lines('entry_points.txt')) + ) + if config.has_section('console_scripts'): + for name, value in config.items('console_scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, name)) + if WINDOWS: + paths_to_remove.add( + os.path.join(bin_dir, name) + '.exe' + ) + paths_to_remove.add( + os.path.join(bin_dir, name) + '.exe.manifest' + ) + paths_to_remove.add( + os.path.join(bin_dir, name) + '-script.py' + ) + + paths_to_remove.remove(auto_confirm) + self.uninstalled = paths_to_remove + + def rollback_uninstall(self): + if self.uninstalled: + self.uninstalled.rollback() + else: + logger.error( + "Can't rollback %s, nothing uninstalled.", self.name, + ) + + def commit_uninstall(self): + if self.uninstalled: + self.uninstalled.commit() + elif not self.nothing_to_uninstall: + logger.error( + "Can't commit %s, nothing uninstalled.", self.name, + ) + + def archive(self, build_dir): + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.pkg_info()["version"]) + archive_path = os.path.join(build_dir, archive_name) + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file %s exists. (i)gnore, (w)ipe, (b)ackup ' % + display_path(archive_path), ('i', 'w', 'b')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + if create_archive: + zip = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, + allowZip64=True + ) + dir = os.path.normcase(os.path.abspath(self.setup_py_dir)) + for dirpath, dirnames, filenames in os.walk(dir): + if 'pip-egg-info' in dirnames: + dirnames.remove('pip-egg-info') + for dirname in dirnames: + dirname = os.path.join(dirpath, dirname) + name = self._clean_zip_name(dirname, dir) + zipdir = zipfile.ZipInfo(self.name + '/' + name + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + filename = os.path.join(dirpath, filename) + name = self._clean_zip_name(filename, dir) + zip.write(filename, self.name + '/' + name) + zip.close() + logger.info('Saved %s', display_path(archive_path)) + + def _clean_zip_name(self, name, prefix): + assert name.startswith(prefix + os.path.sep), ( + "name %r doesn't start with prefix %r" % (name, prefix) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + def match_markers(self): + if self.markers is not None: + from packaging.markers import Marker + return Marker(self.markers).evaluate() + else: + return True + + def install(self, install_options, global_options=[], root=None, + prefix=None): + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix) + return + if self.is_wheel: + version = pip.wheel.wheel_version(self.source_dir) + pip.wheel.check_compatibility(version, self.name) + + self.move_wheel_files(self.source_dir, root=root, prefix=prefix) + self.install_succeeded = True + return + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options += self.options.get('global_options', []) + install_options += self.options.get('install_options', []) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + temp_location = tempfile.mkdtemp('-record', 'pip-') + record_filename = os.path.join(temp_location, 'install-record.txt') + try: + install_args = [sys.executable, "-u"] + install_args.append('-c') + install_args.append(SETUPTOOLS_SHIM % self.setup_py) + install_args += list(global_options) + \ + ['install', '--record', record_filename] + + if not self.as_egg: + install_args += ['--single-version-externally-managed'] + + if root is not None: + install_args += ['--root', root] + if prefix is not None: + install_args += ['--prefix', prefix] + + if self.pycompile: + install_args += ["--compile"] + else: + install_args += ["--no-compile"] + + if running_under_virtualenv(): + py_ver_str = 'python' + sysconfig.get_python_version() + install_args += ['--install-headers', + os.path.join(sys.prefix, 'include', 'site', + py_ver_str, self.name)] + msg = 'Running setup.py install for %s' % (self.name,) + with open_spinner(msg) as spinner: + with indent_log(): + call_subprocess( + install_args + install_options, + cwd=self.setup_py_dir, + show_stdout=False, + spinner=spinner, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + return + self.install_succeeded = True + if self.as_egg: + # there's no --always-unzip option we can pass to install + # command so we unable to save the installed-files.txt + return + + def prepend_root(path): + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + with open(record_filename) as f: + for line in f: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + logger.warning( + 'Could not find .egg-info directory in install record' + ' for %s', + self, + ) + # FIXME: put the record somewhere + # FIXME: should this be an error? + return + new_lines = [] + with open(record_filename) as f: + for line in f: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath( + prepend_root(filename), egg_info_dir) + ) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + finally: + if os.path.exists(record_filename): + os.remove(record_filename) + rmtree(temp_location) + + def ensure_has_source_dir(self, parent_dir): + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.build_location(parent_dir) + return self.source_dir + + def remove_temporary_source(self): + """Remove the source files from this requirement, if they are marked + for deletion""" + if self.source_dir and os.path.exists( + os.path.join(self.source_dir, PIP_DELETE_MARKER_FILENAME)): + logger.debug('Removing source in %s', self.source_dir) + rmtree(self.source_dir) + self.source_dir = None + if self._temp_build_dir and os.path.exists(self._temp_build_dir): + rmtree(self._temp_build_dir) + self._temp_build_dir = None + + def install_editable(self, install_options, + global_options=(), prefix=None): + logger.info('Running setup.py develop for %s', self.name) + + if self.isolated: + global_options = list(global_options) + ["--no-user-cfg"] + + if prefix: + prefix_param = ['--prefix={0}'.format(prefix)] + install_options = list(install_options) + prefix_param + + with indent_log(): + # FIXME: should we do --install-headers here too? + call_subprocess( + [ + sys.executable, + '-c', + SETUPTOOLS_SHIM % self.setup_py + ] + + list(global_options) + + ['develop', '--no-deps'] + + list(install_options), + + cwd=self.setup_py_dir, + show_stdout=False) + + self.install_succeeded = True + + def check_if_exists(self): + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.conflicts_with appropriately. + """ + if self.req is None: + return False + try: + # get_distribution() will resolve the entire list of requirements + # anyway, and we've already determined that we need the requirement + # in question, so strip the marker so that we don't try to + # evaluate it. + from packaging.requirements import Requirement + no_marker = Requirement(str(self.req)) + no_marker.marker = None + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if self.use_user_site: + if dist_in_usersite(existing_dist): + self.conflicts_with = existing_dist + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to %s in %s" % + (existing_dist.project_name, existing_dist.location) + ) + else: + self.conflicts_with = existing_dist + return True + + @property + def is_wheel(self): + return self.link and self.link.is_wheel + + def move_wheel_files(self, wheeldir, root=None, prefix=None): + move_wheel_files( + self.name, self.req, wheeldir, + user=self.use_user_site, + home=self.target_dir, + root=root, + prefix=prefix, + pycompile=self.pycompile, + isolated=self.isolated, + ) + + def get_dist(self): + """Return a pkg_resources.Distribution built from self.egg_info_path""" + egg_info = self.egg_info_path('').rstrip('/') + base_dir = os.path.dirname(egg_info) + metadata = pkg_resources.PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + return pkg_resources.Distribution( + os.path.dirname(egg_info), + project_name=dist_name, + metadata=metadata) + + @property + def has_hash_options(self): + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.options.get('hashes', {})) + + def hashes(self, trust_internet=True): + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.options.get('hashes', {}).copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + +def _strip_postfix(req): + """ + Strip req postfix ( -dev, 0.2, etc ) + """ + # FIXME: use package_to_requirement? + match = re.search(r'^(.*?)(?:-dev|-\d.*)$', req) + if match: + # Strip off -dev, -0.2, etc. + req = match.group(1) + return req + + +def _build_req_from_url(url): + + parts = [p for p in url.split('#', 1)[0].split('/') if p] + + req = None + if len(parts) > 2 and parts[-2] in ('tags', 'branches', 'tag', 'branch'): + req = parts[-3] + elif len(parts) > 1 and parts[-1] == 'trunk': + req = parts[-2] + if req: + warnings.warn( + 'Sniffing the requirement name from the url is deprecated and ' + 'will be removed in the future. Please specify an #egg segment ' + 'instead.', RemovedInPip9Warning, + stacklevel=2) + return req + + +def parse_editable(editable_req, default_vcs=None): + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + from pip.index import Link + + url = editable_req + extras = None + + # If a file path is specified with extras, strip off the extras. + m = re.match(r'^(.+)(\[[^\]]+\])$', url) + if m: + url_no_extras = m.group(1) + extras = m.group(2) + else: + url_no_extras = url + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + raise InstallationError( + "Directory %r is not installable. File 'setup.py' not found." % + url_no_extras + ) + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras).extras, + ) + else: + return package_name, url_no_extras, None + + for version_control in vcs: + if url.lower().startswith('%s:' % version_control): + url = '%s+%s' % (version_control, url) + break + + if '+' not in url: + if default_vcs: + url = default_vcs + '+' + url + else: + raise InstallationError( + '%s should either be a path to a local project or a VCS url ' + 'beginning with svn+, git+, hg+, or bzr+' % + editable_req + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + error_message = 'For --editable=%s only ' % editable_req + \ + ', '.join([backend.name + '+URL' for backend in vcs.backends]) + \ + ' is currently supported' + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + package_name = _build_req_from_url(editable_req) + if not package_name: + raise InstallationError( + '--editable=%s is not the right format; it must have ' + '#egg=Package' % editable_req + ) + return _strip_postfix(package_name), url, None diff --git a/venv/lib/python3.5/site-packages/pip/req/req_set.py b/venv/lib/python3.5/site-packages/pip/req/req_set.py new file mode 100644 index 0000000..03338a7 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/req/req_set.py @@ -0,0 +1,746 @@ +from __future__ import absolute_import + +from collections import defaultdict +from itertools import chain +import logging +import os + +from pip._vendor import pkg_resources +from pip._vendor import requests + +from pip.compat import expanduser +from pip.download import (is_file_url, is_dir_url, is_vcs_url, url_to_path, + unpack_url) +from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled, + DistributionNotFound, PreviousBuildDirError, + HashError, HashErrors, HashUnpinned, + DirectoryUrlHashUnsupported, VcsHashUnsupported) +from pip.req.req_install import InstallRequirement +from pip.utils import ( + display_path, dist_in_usersite, ensure_dir, normalize_path) +from pip.utils.hashes import MissingHashes +from pip.utils.logging import indent_log +from pip.vcs import vcs + + +logger = logging.getLogger(__name__) + + +class Requirements(object): + + def __init__(self): + self._keys = [] + self._dict = {} + + def keys(self): + return self._keys + + def values(self): + return [self._dict[key] for key in self._keys] + + def __contains__(self, item): + return item in self._keys + + def __setitem__(self, key, value): + if key not in self._keys: + self._keys.append(key) + self._dict[key] = value + + def __getitem__(self, key): + return self._dict[key] + + def __repr__(self): + values = ['%s: %s' % (repr(k), repr(self[k])) for k in self.keys()] + return 'Requirements({%s})' % ', '.join(values) + + +class DistAbstraction(object): + """Abstracts out the wheel vs non-wheel prepare_files logic. + + The requirements for anything installable are as follows: + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + - we must be able to generate a list of run-time dependencies + without installing any additional packages (or we would + have to either burn time by doing temporary isolated installs + or alternatively violate pips 'don't start installing unless + all requirements are available' rule - neither of which are + desirable). + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req_to_install): + self.req_to_install = req_to_install + + def dist(self, finder): + """Return a setuptools Dist object.""" + raise NotImplementedError(self.dist) + + def prep_for_dist(self): + """Ensure that we can get a Dist for this requirement.""" + raise NotImplementedError(self.dist) + + +def make_abstract_dist(req_to_install): + """Factory to make an abstract dist object. + + Preconditions: Either an editable req with a source_dir, or satisfied_by or + a wheel link, or a non-editable req with a source_dir. + + :return: A concrete DistAbstraction. + """ + if req_to_install.editable: + return IsSDist(req_to_install) + elif req_to_install.link and req_to_install.link.is_wheel: + return IsWheel(req_to_install) + else: + return IsSDist(req_to_install) + + +class IsWheel(DistAbstraction): + + def dist(self, finder): + return list(pkg_resources.find_distributions( + self.req_to_install.source_dir))[0] + + def prep_for_dist(self): + # FIXME:https://github.com/pypa/pip/issues/1112 + pass + + +class IsSDist(DistAbstraction): + + def dist(self, finder): + dist = self.req_to_install.get_dist() + # FIXME: shouldn't be globally added: + if dist.has_metadata('dependency_links.txt'): + finder.add_dependency_links( + dist.get_metadata_lines('dependency_links.txt') + ) + return dist + + def prep_for_dist(self): + self.req_to_install.run_egg_info() + self.req_to_install.assert_source_matches_version() + + +class Installed(DistAbstraction): + + def dist(self, finder): + return self.req_to_install.satisfied_by + + def prep_for_dist(self): + pass + + +class RequirementSet(object): + + def __init__(self, build_dir, src_dir, download_dir, upgrade=False, + ignore_installed=False, as_egg=False, target_dir=None, + ignore_dependencies=False, force_reinstall=False, + use_user_site=False, session=None, pycompile=True, + isolated=False, wheel_download_dir=None, + wheel_cache=None, require_hashes=False): + """Create a RequirementSet. + + :param wheel_download_dir: Where still-packed .whl files should be + written to. If None they are written to the download_dir parameter. + Separate to download_dir to permit only keeping wheel archives for + pip wheel. + :param download_dir: Where still packed archives should be written to. + If None they are not saved, and are deleted immediately after + unpacking. + :param wheel_cache: The pip wheel cache, for passing to + InstallRequirement. + """ + if session is None: + raise TypeError( + "RequirementSet() missing 1 required keyword argument: " + "'session'" + ) + + self.build_dir = build_dir + self.src_dir = src_dir + # XXX: download_dir and wheel_download_dir overlap semantically and may + # be combined if we're willing to have non-wheel archives present in + # the wheelhouse output by 'pip wheel'. + self.download_dir = download_dir + self.upgrade = upgrade + self.ignore_installed = ignore_installed + self.force_reinstall = force_reinstall + self.requirements = Requirements() + # Mapping of alias: real_name + self.requirement_aliases = {} + self.unnamed_requirements = [] + self.ignore_dependencies = ignore_dependencies + self.successfully_downloaded = [] + self.successfully_installed = [] + self.reqs_to_cleanup = [] + self.as_egg = as_egg + self.use_user_site = use_user_site + self.target_dir = target_dir # set from --target option + self.session = session + self.pycompile = pycompile + self.isolated = isolated + if wheel_download_dir: + wheel_download_dir = normalize_path(wheel_download_dir) + self.wheel_download_dir = wheel_download_dir + self._wheel_cache = wheel_cache + self.require_hashes = require_hashes + # Maps from install_req -> dependencies_of_install_req + self._dependencies = defaultdict(list) + + def __str__(self): + reqs = [req for req in self.requirements.values() + if not req.comes_from] + reqs.sort(key=lambda req: req.name.lower()) + return ' '.join([str(req.req) for req in reqs]) + + def __repr__(self): + reqs = [req for req in self.requirements.values()] + reqs.sort(key=lambda req: req.name.lower()) + reqs_str = ', '.join([str(req.req) for req in reqs]) + return ('<%s object; %d requirement(s): %s>' + % (self.__class__.__name__, len(reqs), reqs_str)) + + def add_requirement(self, install_req, parent_req_name=None): + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + name = install_req.name + if not install_req.match_markers(): + logger.warning("Ignoring %s: markers %r don't match your " + "environment", install_req.name, + install_req.markers) + return [] + + install_req.as_egg = self.as_egg + install_req.use_user_site = self.use_user_site + install_req.target_dir = self.target_dir + install_req.pycompile = self.pycompile + if not name: + # url or path requirement w/o an egg fragment + self.unnamed_requirements.append(install_req) + return [install_req] + else: + try: + existing_req = self.get_requirement(name) + except KeyError: + existing_req = None + if (parent_req_name is None and existing_req and not + existing_req.constraint and + existing_req.extras == install_req.extras and not + existing_req.req.specifier == install_req.req.specifier): + raise InstallationError( + 'Double requirement given: %s (already in %s, name=%r)' + % (install_req, existing_req, name)) + if not existing_req: + # Add requirement + self.requirements[name] = install_req + # FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + result = [install_req] + else: + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + result = [] + if not install_req.constraint and existing_req.constraint: + if (install_req.link and not (existing_req.link and + install_req.link.path == existing_req.link.path)): + self.reqs_to_cleanup.append(install_req) + raise InstallationError( + "Could not satisfy constraints for '%s': " + "installation from path or url cannot be " + "constrained to a version" % name) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + existing_req.extras = tuple( + sorted(set(existing_req.extras).union( + set(install_req.extras)))) + logger.debug("Setting %s extras to: %s", + existing_req, existing_req.extras) + # And now we need to scan this. + result = [existing_req] + # Canonicalise to the already-added object for the backref + # check below. + install_req = existing_req + if parent_req_name: + parent_req = self.get_requirement(parent_req_name) + self._dependencies[parent_req].append(install_req) + return result + + def has_requirement(self, project_name): + name = project_name.lower() + if (name in self.requirements and + not self.requirements[name].constraint or + name in self.requirement_aliases and + not self.requirements[self.requirement_aliases[name]].constraint): + return True + return False + + @property + def has_requirements(self): + return list(req for req in self.requirements.values() if not + req.constraint) or self.unnamed_requirements + + @property + def is_download(self): + if self.download_dir: + self.download_dir = expanduser(self.download_dir) + if os.path.exists(self.download_dir): + return True + else: + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '%s'" + % display_path(self.download_dir)) + return False + + def get_requirement(self, project_name): + for name in project_name, project_name.lower(): + if name in self.requirements: + return self.requirements[name] + if name in self.requirement_aliases: + return self.requirements[self.requirement_aliases[name]] + raise KeyError("No project with the name %r" % project_name) + + def uninstall(self, auto_confirm=False): + for req in self.requirements.values(): + if req.constraint: + continue + req.uninstall(auto_confirm=auto_confirm) + req.commit_uninstall() + + def prepare_files(self, finder): + """ + Prepare process. Create temp directories, download and/or unpack files. + """ + # make the wheelhouse + if self.wheel_download_dir: + ensure_dir(self.wheel_download_dir) + + # If any top-level requirement has a hash specified, enter + # hash-checking mode, which requires hashes from all. + root_reqs = self.unnamed_requirements + self.requirements.values() + require_hashes = (self.require_hashes or + any(req.has_hash_options for req in root_reqs)) + if require_hashes and self.as_egg: + raise InstallationError( + '--egg is not allowed with --require-hashes mode, since it ' + 'delegates dependency resolution to setuptools and could thus ' + 'result in installation of unhashed packages.') + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # req.populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] + hash_errors = HashErrors() + for req in chain(root_reqs, discovered_reqs): + try: + discovered_reqs.extend(self._prepare_file( + finder, + req, + require_hashes=require_hashes, + ignore_dependencies=self.ignore_dependencies)) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + def _check_skip_installed(self, req_to_install, finder): + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + # Check whether to upgrade/reinstall this req or not. + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + skip_reason = 'satisfied (use --upgrade to upgrade)' + if self.upgrade: + best_installed = False + # For link based requirements we have to pull the + # tree down and inspect to assess the version #, so + # its handled way down. + if not (self.force_reinstall or req_to_install.link): + try: + finder.find_requirement(req_to_install, self.upgrade) + except BestVersionAlreadyInstalled: + skip_reason = 'up-to-date' + best_installed = True + except DistributionNotFound: + # No distribution found, so we squash the + # error - it will be raised later when we + # re-try later to do the install. + # Why don't we just raise here? + pass + + if not best_installed: + # don't uninstall conflict if user install and + # conflict is not user install + if not (self.use_user_site and not + dist_in_usersite(req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + return skip_reason + else: + return None + + def _prepare_file(self, + finder, + req_to_install, + require_hashes=False, + ignore_dependencies=False): + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # ###################### # + # # print log messages # # + # ###################### # + if req_to_install.editable: + logger.info('Obtaining %s', req_to_install) + else: + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req_to_install.satisfied_by is None + if not self.ignore_installed: + skip_reason = self._check_skip_installed( + req_to_install, finder) + + if req_to_install.satisfied_by: + assert skip_reason is not None, ( + '_check_skip_installed returned None but ' + 'req_to_install.satisfied_by is set to %r' + % (req_to_install.satisfied_by,)) + logger.info( + 'Requirement already %s: %s', skip_reason, + req_to_install) + else: + if (req_to_install.link and + req_to_install.link.scheme == 'file'): + path = url_to_path(req_to_install.link.url) + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req_to_install) + + with indent_log(): + # ################################ # + # # vcs update or unpack archive # # + # ################################ # + if req_to_install.editable: + if require_hashes: + raise InstallationError( + 'The editable requirement %s cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.' % req_to_install) + req_to_install.ensure_has_source_dir(self.src_dir) + req_to_install.update_editable(not self.is_download) + abstract_dist = make_abstract_dist(req_to_install) + abstract_dist.prep_for_dist() + if self.is_download: + req_to_install.archive(self.download_dir) + elif req_to_install.satisfied_by: + if require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.') + abstract_dist = Installed(req_to_install) + else: + # @@ if filesystem packages are not marked + # editable in a req, a non deterministic error + # occurs when the script attempts to unpack the + # build directory + req_to_install.ensure_has_source_dir(self.build_dir) + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req_to_install.source_dir` + if os.path.exists( + os.path.join(req_to_install.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '%s' due to a" + " pre-existing build directory (%s). This is " + "likely due to a previous installation that failed" + ". pip is being responsible and not assuming it " + "can delete this. Please delete it and try again." + % (req_to_install, req_to_install.source_dir) + ) + req_to_install.populate_link( + finder, self.upgrade, require_hashes) + # We can't hit this spot and have populate_link return None. + # req_to_install.satisfied_by is None here (because we're + # guarded) and upgrade has no impact except when satisfied_by + # is not None. + # Then inside find_requirement existing_applicable -> False + # If no new versions are found, DistributionNotFound is raised, + # otherwise a result is guaranteed. + assert req_to_install.link + link = req_to_install.link + + # Now that we have the real link, we can tell what kind of + # requirements we have and raise some more informative errors + # than otherwise. (For example, we can raise VcsHashUnsupported + # for a VCS URL rather than HashMissing.) + if require_hashes: + # We could check these first 2 conditions inside + # unpack_url and save repetition of conditions, but then + # we would report less-useful error messages for + # unhashable requirements, complaining that there's no + # hash provided. + if is_vcs_url(link): + raise VcsHashUnsupported() + elif is_file_url(link) and is_dir_url(link): + raise DirectoryUrlHashUnsupported() + if (not req_to_install.original_link and + not req_to_install.is_pinned): + # Unpinned packages are asking for trouble when a new + # version is uploaded. This isn't a security check, but + # it saves users a surprising hash mismatch in the + # future. + # + # file:/// URLs aren't pinnable, so don't complain + # about them not being pinned. + raise HashUnpinned() + hashes = req_to_install.hashes( + trust_internet=not require_hashes) + if require_hashes and not hashes: + # Known-good hashes are missing for this requirement, so + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + hashes = MissingHashes() + + try: + download_dir = self.download_dir + # We always delete unpacked sdists after pip ran. + autodelete_unpacked = True + if req_to_install.link.is_wheel \ + and self.wheel_download_dir: + # when doing 'pip wheel` we download wheels to a + # dedicated dir. + download_dir = self.wheel_download_dir + if req_to_install.link.is_wheel: + if download_dir: + # When downloading, we only unpack wheels to get + # metadata. + autodelete_unpacked = True + else: + # When installing a wheel, we use the unpacked + # wheel. + autodelete_unpacked = False + unpack_url( + req_to_install.link, req_to_install.source_dir, + download_dir, autodelete_unpacked, + session=self.session, hashes=hashes) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because ' + 'of error %s', + req_to_install, + exc, + ) + raise InstallationError( + 'Could not install requirement %s because ' + 'of HTTP error %s for URL %s' % + (req_to_install, exc, req_to_install.link) + ) + abstract_dist = make_abstract_dist(req_to_install) + abstract_dist.prep_for_dist() + if self.is_download: + # Make a .zip of the source_dir we already created. + if req_to_install.link.scheme in vcs.all_schemes: + req_to_install.archive(self.download_dir) + # req_to_install.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req_to_install.check_if_exists() + if req_to_install.satisfied_by: + if self.upgrade or self.ignore_installed: + # don't uninstall conflict if user install and + # conflict is not user install + if not (self.use_user_site and not + dist_in_usersite( + req_to_install.satisfied_by)): + req_to_install.conflicts_with = \ + req_to_install.satisfied_by + req_to_install.satisfied_by = None + else: + logger.info( + 'Requirement already satisfied (use ' + '--upgrade to upgrade): %s', + req_to_install, + ) + + # ###################### # + # # parse dependencies # # + # ###################### # + dist = abstract_dist.dist(finder) + more_reqs = [] + + def add_req(subreq): + sub_install_req = InstallRequirement( + str(subreq), + req_to_install, + isolated=self.isolated, + wheel_cache=self._wheel_cache, + ) + more_reqs.extend(self.add_requirement( + sub_install_req, req_to_install.name)) + + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not self.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + self.add_requirement(req_to_install, None) + + if not ignore_dependencies: + if (req_to_install.extras): + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + '%s does not provide the extra \'%s\'', + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq) + + # cleanup tmp src + self.reqs_to_cleanup.append(req_to_install) + + if not req_to_install.editable and not req_to_install.satisfied_by: + # XXX: --no-install leads this to report 'Successfully + # downloaded' for only non-editable reqs, even though we took + # action on them. + self.successfully_downloaded.append(req_to_install) + + return more_reqs + + def cleanup_files(self): + """Clean up files, remove builds.""" + logger.debug('Cleaning up...') + with indent_log(): + for req in self.reqs_to_cleanup: + req.remove_temporary_source() + + def _to_install(self): + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._dependencies[req]: + schedule(dep) + order.append(req) + for install_req in self.requirements.values(): + schedule(install_req) + return order + + def install(self, install_options, global_options=(), *args, **kwargs): + """ + Install everything in this set (after having downloaded and unpacked + the packages) + """ + to_install = self._to_install() + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join([req.name for req in to_install]), + ) + + with indent_log(): + for requirement in to_install: + if requirement.conflicts_with: + logger.info( + 'Found existing installation: %s', + requirement.conflicts_with, + ) + with indent_log(): + requirement.uninstall(auto_confirm=True) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except: + # if install did not succeed, rollback previous uninstall + if (requirement.conflicts_with and not + requirement.install_succeeded): + requirement.rollback_uninstall() + raise + else: + if (requirement.conflicts_with and + requirement.install_succeeded): + requirement.commit_uninstall() + requirement.remove_temporary_source() + + self.successfully_installed = to_install diff --git a/venv/lib/python3.5/site-packages/pip/req/req_uninstall.py b/venv/lib/python3.5/site-packages/pip/req/req_uninstall.py new file mode 100644 index 0000000..5248430 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/req/req_uninstall.py @@ -0,0 +1,195 @@ +from __future__ import absolute_import + +import logging +import os +import tempfile + +from pip.compat import uses_pycache, WINDOWS, cache_from_source +from pip.exceptions import UninstallationError +from pip.utils import rmtree, ask, is_local, renames, normalize_path +from pip.utils.logging import indent_log + + +logger = logging.getLogger(__name__) + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + self.paths = set() + self._refuse = set() + self.pth = {} + self.dist = dist + self.save_dir = None + self._moved_paths = [] + + def _permitted(self, path): + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def compact(self, paths): + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + short_paths = set() + for path in sorted(paths, key=len): + if not any([ + (path.startswith(shortpath) and + path[len(shortpath.rstrip(os.path.sep))] == os.path.sep) + for shortpath in short_paths]): + short_paths.add(path) + return short_paths + + def _stash(self, path): + return os.path.join( + self.save_dir, os.path.splitdrive(path)[1].lstrip(os.path.sep)) + + def remove(self, auto_confirm=False): + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + logger.info( + 'Uninstalling %s-%s:', + self.dist.project_name, self.dist.version + ) + + with indent_log(): + paths = sorted(self.compact(self.paths)) + + if auto_confirm: + response = 'y' + else: + for path in paths: + logger.info(path) + response = ask('Proceed (y/n)? ', ('y', 'n')) + if self._refuse: + logger.info('Not removing or modifying (outside of prefix):') + for path in self.compact(self._refuse): + logger.info(path) + if response == 'y': + self.save_dir = tempfile.mkdtemp(suffix='-uninstall', + prefix='pip-') + for path in paths: + new_path = self._stash(path) + logger.debug('Removing file or directory %s', path) + self._moved_paths.append(path) + renames(path, new_path) + for pth in self.pth.values(): + pth.remove() + logger.info( + 'Successfully uninstalled %s-%s', + self.dist.project_name, self.dist.version + ) + + def rollback(self): + """Rollback the changes previously made by remove().""" + if self.save_dir is None: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return False + logger.info('Rolling back uninstall of %s', self.dist.project_name) + for path in self._moved_paths: + tmp_path = self._stash(path) + logger.debug('Replacing %s', path) + renames(tmp_path, path) + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + """Remove temporary save dir: rollback will no longer be possible.""" + if self.save_dir is not None: + rmtree(self.save_dir) + self.save_dir = None + self._moved_paths = [] + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + if not os.path.isfile(pth_file): + raise UninstallationError( + "Cannot remove entries from nonexistent file %s" % pth_file + ) + self.file = pth_file + self.entries = set() + self._saved_lines = None + + def add(self, entry): + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + logger.debug('Removing pth entries from %s:', self.file) + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/venv/lib/python3.5/site-packages/pip/status_codes.py b/venv/lib/python3.5/site-packages/pip/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/status_codes.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/venv/lib/python3.5/site-packages/pip/utils/__init__.py b/venv/lib/python3.5/site-packages/pip/utils/__init__.py new file mode 100644 index 0000000..8ea2e38 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/__init__.py @@ -0,0 +1,878 @@ +from __future__ import absolute_import + +from collections import deque +import contextlib +import errno +import io +import locale +# we have a submodule named 'logging' which would shadow this if we used the +# regular name: +import logging as std_logging +import re +import os +import posixpath +import shutil +import stat +import subprocess +import sys +import tarfile +import zipfile + +from pip.exceptions import InstallationError +from pip.compat import console_to_str, expanduser, stdlib_pkgs +from pip.locations import ( + site_packages, user_site, running_under_virtualenv, virtualenv_no_global, + write_delete_marker_file, +) +from pip._vendor import pkg_resources +from pip._vendor.six.moves import input +from pip._vendor.six import PY2 +from pip._vendor.retrying import retry + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'is_svn_page', 'file_contents', + 'split_leading_dir', 'has_leading_dir', + 'normalize_path', + 'renames', 'get_terminal_size', 'get_prog', + 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'captured_stdout', 'remove_tracebacks', 'ensure_dir', + 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', + 'get_installed_version'] + + +logger = std_logging.getLogger(__name__) + +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') +ZIP_EXTENSIONS = ('.zip', '.whl') +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS) +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def import_or_raise(pkg_or_module_string, ExceptionType, *args, **kwargs): + try: + return __import__(pkg_or_module_string) + except ImportError: + raise ExceptionType(*args, **kwargs) + + +def ensure_dir(path): + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def get_prog(): + try: + if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + # if file type currently read only + if os.stat(path).st_mode & stat.S_IREAD: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def ask(message, options): + """Ask the message interactively, with the given possible responses""" + while 1: + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: %s' % + message + ) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response (%r) was not one of the expected responses: ' + '%s' % (response, ', '.join(options)) + ) + else: + return response + + +def format_size(bytes): + if bytes > 1000 * 1000: + return '%.1fMB' % (bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '%ikB' % (bytes / 1000) + elif bytes > 1000: + return '%.1fkB' % (bytes / 1000.0) + else: + return '%ibytes' % bytes + + +def is_installable_dir(path): + """Return True if `path` is a directory containing a setup.py file.""" + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + return False + + +def is_svn_page(html): + """ + Returns true if the page appears to be the index page of an svn repository + """ + return (re.search(r'[^<]*Revision \d+:', html) and + re.search(r'Powered by (?:<a[^>]*?>)?Subversion', html, re.I)) + + +def file_contents(filename): + with open(filename, 'rb') as fp: + return fp.read().decode('utf-8') + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def split_leading_dir(path): + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def normalize_path(path, resolve_symlinks=True): + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + """ + Return True if this is a path pip is allowed to modify. + + If we're in a virtualenv, sys.prefix points to the virtualenv's + prefix; only sys.prefix is considered local. + + If we're not in a virtualenv, in general we can modify anything. + However, if the OS vendor has configured distutils to install + somewhere other than sys.prefix (which could be a subdirectory of + sys.prefix, e.g. /usr/local), we consider sys.prefix itself nonlocal + and the domain of the OS vendor. (In other words, everything _other + than_ sys.prefix is considered local.) + + """ + + path = normalize_path(path) + prefix = normalize_path(sys.prefix) + + if running_under_virtualenv(): + return path.startswith(normalize_path(sys.prefix)) + else: + from pip.locations import distutils_scheme + if path.startswith(prefix): + for local_path in distutils_scheme("").values(): + if path.startswith(normalize_path(local_path)): + return True + return False + else: + return True + + +def dist_is_local(dist): + """ + Return True if given Distribution object is installed somewhere pip + is allowed to modify. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + """ + Return True if given Distribution is installed in user site. + """ + norm_path = normalize_path(dist_location(dist)) + return norm_path.startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + """ + Return True if given Distribution is installed in + distutils.sysconfig.get_python_lib(). + """ + return normalize_path( + dist_location(dist) + ).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + """Is distribution an editable install?""" + for path_item in sys.path: + egg_link = os.path.join(path_item, dist.project_name + '.egg-link') + if os.path.isfile(egg_link): + return True + return False + + +def get_installed_distributions(local_only=True, + skip=stdlib_pkgs, + include_editables=True, + editables_only=False, + user_only=False): + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + """ + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in pkg_resources.working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def egg_link_path(dist): + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + if virtualenv_no_global(): + sites.append(site_packages) + else: + sites.append(site_packages) + if user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + + +def dist_location(dist): + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + """ + egg_link = egg_link_path(dist) + if egg_link: + return egg_link + return dist.location + + +def get_terminal_size(): + """Returns a tuple (x, y) representing the width(x) and the height(x) + in characters of the terminal window.""" + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234') + ) + except: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) + + +def current_umask(): + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def unzip_file(filename, location, flatten=True): + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + if mode and stat.S_ISREG(mode) and mode & 0o111: + # make dest file have execute for user/group/world + # (chmod +x) no-op on windows per python docs + os.chmod(fn, (0o777 - current_umask() | 0o111)) + finally: + zipfp.close() + + +def untar_file(filename, location): + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + # note: python<=2.5 doesn't seem to know about pax headers, filter them + leading = has_leading_dir([ + member.name for member in tar.getmembers() + if member.name != 'pax_global_header' + ]) + for member in tar.getmembers(): + fn = member.name + if fn == 'pax_global_header': + continue + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + tar._extract_member(member, path) + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + tar.utime(member, path) + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + # make dest file have execute for user/group/world + # no-op on windows per python docs + os.chmod(path, (0o777 - current_umask() | 0o111)) + finally: + tar.close() + + +def unpack_file(filename, location, content_type, link): + filename = os.path.realpath(filename) + if (content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename)): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif (content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)): + untar_file(filename, location) + elif (content_type and content_type.startswith('text/html') and + is_svn_page(file_contents(filename))): + # We don't really care about this + from pip.vcs.subversion import Subversion + Subversion('svn+' + link.url).unpack(location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of %s' % location + ) + + +def remove_tracebacks(output): + pattern = (r'(?:\W+File "(?:.*)", line (?:.*)\W+(?:.*)\W+\^\W+)?' + r'Syntax(?:Error|Warning): (?:.*)') + output = re.sub(pattern, '', output) + if PY2: + return output + # compileall.compile_dir() prints different messages to stdout + # in Python 3 + return re.sub(r"\*\*\* Error compiling (?:.*)", '', output) + + +def call_subprocess(cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_level=std_logging.DEBUG, command_desc=None, + extra_environ=None, spinner=None): + # This function's handling of subprocess output is confusing and I + # previously broke it terribly, so as penance I will write a long comment + # explaining things. + # + # The obvious thing that affects output is the show_stdout= + # kwarg. show_stdout=True means, let the subprocess write directly to our + # stdout. Even though it is nominally the default, it is almost never used + # inside pip (and should not be used in new code without a very good + # reason); as of 2016-02-22 it is only used in a few places inside the VCS + # wrapper code. Ideally we should get rid of it entirely, because it + # creates a lot of complexity here for a rarely used feature. + # + # Most places in pip set show_stdout=False. What this means is: + # - We connect the child stdout to a pipe, which we read. + # - By default, we hide the output but show a spinner -- unless the + # subprocess exits with an error, in which case we show the output. + # - If the --verbose option was passed (= loglevel is DEBUG), then we show + # the output unconditionally. (But in this case we don't want to show + # the output a second time if it turns out that there was an error.) + # + # stderr is always merged with stdout (even if show_stdout=True). + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + if command_desc is None: + cmd_parts = [] + for part in cmd: + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + command_desc = ' '.join(cmd_parts) + logger.log(command_level, "Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, + cwd=cwd, env=env) + except Exception as exc: + logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + if stdout is not None: + all_output = [] + while True: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + if logger.getEffectiveLevel() <= std_logging.DEBUG: + # Show the line immediately + logger.debug(line) + else: + # Update the spinner + if spinner is not None: + spinner.spin() + proc.wait() + if spinner is not None: + if proc.returncode: + spinner.finish("error") + else: + spinner.finish("done") + if proc.returncode: + if on_returncode == 'raise': + if (logger.getEffectiveLevel() > std_logging.DEBUG and + not show_stdout): + logger.info( + 'Complete output from command %s:', command_desc, + ) + logger.info( + ''.join(all_output) + + '\n----------------------------------------' + ) + raise InstallationError( + 'Command "%s" failed with error code %s in %s' + % (command_desc, proc.returncode, cwd)) + elif on_returncode == 'warn': + logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, proc.returncode, cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode=%s' % + repr(on_returncode)) + if not show_stdout: + return remove_tracebacks(''.join(all_output)) + + +def read_text_file(filename): + """Return the contents of *filename*. + + Try to decode the file contents with utf-8, the preferred system encoding + (e.g., cp1252 on some Windows machines), and latin1, in that order. + Decoding a byte string with latin1 will never raise an error. In the worst + case, the returned string will contain some garbage characters. + + """ + with open(filename, 'rb') as fp: + data = fp.read() + + encodings = ['utf-8', locale.getpreferredencoding(False), 'latin1'] + for enc in encodings: + try: + data = data.decode(enc) + except UnicodeDecodeError: + continue + break + + assert type(data) != bytes # Latin1 should have worked. + return data + + +def _make_build_dir(build_dir): + os.makedirs(build_dir) + write_delete_marker_file(build_dir) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = (l for l in lines) + + def readline(self): + try: + try: + return next(self._gen) + except NameError: + return self._gen.next() + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +class cached_property(object): + """A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. + + Source: https://github.com/bottlepy/bottle/blob/0.11.5/bottle.py#L175 + """ + + def __init__(self, func): + self.__doc__ = getattr(func, '__doc__') + self.func = func + + def __get__(self, obj, cls): + if obj is None: + # We're being accessed from the class itself, not from an object + return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +def get_installed_version(dist_name): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + # We want to avoid having this cached, so we need to construct a new + # working set each time. + working_set = pkg_resources.WorkingSet() + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) diff --git a/venv/lib/python3.5/site-packages/pip/utils/appdirs.py b/venv/lib/python3.5/site-packages/pip/utils/appdirs.py new file mode 100644 index 0000000..60ae76e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/appdirs.py @@ -0,0 +1,224 @@ +""" +This code was taken from https://github.com/ActiveState/appdirs and modified +to suit our purposes. +""" +from __future__ import absolute_import + +import os +import sys + +from pip.compat import WINDOWS, expanduser + + +def user_cache_dir(appname): + r""" + Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Windows: C:\Users\<username>\AppData\Local\<AppName>\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go + in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the + non-roaming app data dir (the default returned by `user_data_dir`). Apps + typically put cache data somewhere *under* the given dir here. Some + examples: + ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache + ...\Acme\SuperApp\Cache\1.0 + + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + """ + if WINDOWS: + # Get the base path + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + + # Add our app name and Cache directory to it + path = os.path.join(path, appname, "Cache") + elif sys.platform == "darwin": + # Get the base path + path = expanduser("~/Library/Caches") + + # Add our app name to it + path = os.path.join(path, appname) + else: + # Get the base path + path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) + + # Add our app name to it + path = os.path.join(path, appname) + + return path + + +def user_data_dir(appname, roaming=False): + """ + Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/<AppName> + Unix: ~/.local/share/<AppName> # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\<username>\ ... + ...Application Data\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local ... + ...Settings\Application Data\<AppName> + Win 7 (not roaming): C:\\Users\<username>\AppData\Local\<AppName> + Win 7 (roaming): C:\\Users\<username>\AppData\Roaming\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if WINDOWS: + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) + elif sys.platform == "darwin": + path = os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) + else: + path = os.path.join( + os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), + appname, + ) + + return path + + +def user_config_dir(appname, roaming=True): + """Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "roaming" (boolean, default True) can be set False to not use the + Windows roaming appdata directory. That means that for users on a + Windows network setup for roaming profiles, this user data will be + sync'd on login. See + <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx> + for a discussion of issues. + + Typical user data directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/<AppName> + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by deafult "~/.config/<AppName>". + """ + if WINDOWS: + path = user_data_dir(appname, roaming=roaming) + elif sys.platform == "darwin": + path = user_data_dir(appname) + else: + path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) + path = os.path.join(path, appname) + + return path + + +# for the discussion regarding site_config_dirs locations +# see <https://github.com/pypa/pip/issues/1733> +def site_config_dirs(appname): + """Return a list of potential user-shared config dirs for this application. + + "appname" is the name of application. + + Typical user config directories are: + Mac OS X: /Library/Application Support/<AppName>/ + Unix: /etc or $XDG_CONFIG_DIRS[i]/<AppName>/ for each value in + $XDG_CONFIG_DIRS + Win XP: C:\Documents and Settings\All Users\Application ... + ...Data\<AppName>\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory + on Vista.) + Win 7: Hidden, but writeable on Win 7: + C:\ProgramData\<AppName>\ + """ + if WINDOWS: + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + pathlist = [os.path.join(path, appname)] + elif sys.platform == 'darwin': + pathlist = [os.path.join('/Library/Application Support', appname)] + else: + # try looking in $XDG_CONFIG_DIRS + xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + if xdg_config_dirs: + pathlist = [ + os.path.join(expanduser(x), appname) + for x in xdg_config_dirs.split(os.pathsep) + ] + else: + pathlist = [] + + # always look in /etc directly as well + pathlist.append('/etc') + + return pathlist + + +# -- Windows support functions -- + +def _get_win_folder_from_registry(csidl_name): + """ + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + directory, _type = _winreg.QueryValueEx(key, shell_folder_name) + return directory + + +def _get_win_folder_with_ctypes(csidl_name): + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # <http://bugs.activestate.com/show_bug.cgi?id=85099>. + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +if WINDOWS: + try: + import ctypes + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + _get_win_folder = _get_win_folder_from_registry diff --git a/venv/lib/python3.5/site-packages/pip/utils/build.py b/venv/lib/python3.5/site-packages/pip/utils/build.py new file mode 100644 index 0000000..fc65cfa --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/build.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import + +import os.path +import tempfile + +from pip.utils import rmtree + + +class BuildDirectory(object): + + def __init__(self, name=None, delete=None): + # If we were not given an explicit directory, and we were not given an + # explicit delete option, then we'll default to deleting. + if name is None and delete is None: + delete = True + + if name is None: + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + name = os.path.realpath(tempfile.mkdtemp(prefix="pip-build-")) + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + if delete is None: + delete = True + + self.name = name + self.delete = delete + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self.delete: + rmtree(self.name) diff --git a/venv/lib/python3.5/site-packages/pip/utils/deprecation.py b/venv/lib/python3.5/site-packages/pip/utils/deprecation.py new file mode 100644 index 0000000..2fb1d1e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/deprecation.py @@ -0,0 +1,76 @@ +""" +A module that implments tooling to enable easy warnings about deprecations. +""" +from __future__ import absolute_import + +import logging +import warnings + + +class PipDeprecationWarning(Warning): + pass + + +class Pending(object): + pass + + +class RemovedInPip9Warning(PipDeprecationWarning): + pass + + +class RemovedInPip10Warning(PipDeprecationWarning, Pending): + pass + + +class Python26DeprecationWarning(PipDeprecationWarning, Pending): + pass + + +# Warnings <-> Logging Integration + + +_warnings_showwarning = None + + +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _warnings_showwarning is not None: + _warnings_showwarning( + message, category, filename, lineno, file, line, + ) + else: + if issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip.deprecations") + + # This is purposely using the % formatter here instead of letting + # the logging module handle the interpolation. This is because we + # want it to appear as if someone typed this entire message out. + log_message = "DEPRECATION: %s" % message + + # PipDeprecationWarnings that are Pending still have at least 2 + # versions to go until they are removed so they can just be + # warnings. Otherwise, they will be removed in the very next + # version of pip. We want these to be more obvious so we use the + # ERROR logging level. + if issubclass(category, Pending): + logger.warning(log_message) + else: + logger.error(log_message) + else: + _warnings_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _warnings_showwarning + + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = _showwarning diff --git a/venv/lib/python3.5/site-packages/pip/utils/encoding.py b/venv/lib/python3.5/site-packages/pip/utils/encoding.py new file mode 100644 index 0000000..2483168 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/encoding.py @@ -0,0 +1,31 @@ +import codecs +import locale +import re + + +BOMS = [ + (codecs.BOM_UTF8, 'utf8'), + (codecs.BOM_UTF16, 'utf16'), + (codecs.BOM_UTF16_BE, 'utf16-be'), + (codecs.BOM_UTF16_LE, 'utf16-le'), + (codecs.BOM_UTF32, 'utf32'), + (codecs.BOM_UTF32_BE, 'utf32-be'), + (codecs.BOM_UTF32_LE, 'utf32-le'), +] + +ENCODING_RE = re.compile(b'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + encoding = ENCODING_RE.search(line).groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode(locale.getpreferredencoding(False)) diff --git a/venv/lib/python3.5/site-packages/pip/utils/filesystem.py b/venv/lib/python3.5/site-packages/pip/utils/filesystem.py new file mode 100644 index 0000000..25ad516 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/filesystem.py @@ -0,0 +1,28 @@ +import os +import os.path + +from pip.compat import get_path_uid + + +def check_path_owner(path): + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if not hasattr(os, "geteuid"): + return True + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) diff --git a/venv/lib/python3.5/site-packages/pip/utils/hashes.py b/venv/lib/python3.5/site-packages/pip/utils/hashes.py new file mode 100644 index 0000000..9602970 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/hashes.py @@ -0,0 +1,92 @@ +from __future__ import absolute_import + +import hashlib + +from pip.exceptions import HashMismatch, HashMissing, InstallationError +from pip.utils import read_chunks +from pip._vendor.six import iteritems, iterkeys, itervalues + + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + self._allowed = {} if hashes is None else hashes + + def check_against_chunks(self, chunks): + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError('Unknown hash name: %s' % hash_name) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + return self.__nonzero__() + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/venv/lib/python3.5/site-packages/pip/utils/logging.py b/venv/lib/python3.5/site-packages/pip/utils/logging.py new file mode 100644 index 0000000..1c1053a --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/logging.py @@ -0,0 +1,130 @@ +from __future__ import absolute_import + +import contextlib +import logging +import logging.handlers +import os + +try: + import threading +except ImportError: + import dummy_threading as threading + +from pip.compat import WINDOWS +from pip.utils import ensure_dir + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +_log_state = threading.local() +_log_state.indentation = 0 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log messages + by our current indentation level. + """ + formatted = logging.Formatter.format(self, record) + formatted = "".join([ + (" " * get_indentation()) + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(colorama.Fore.RED)), + (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None): + logging.StreamHandler.__init__(self, stream) + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def should_color(self): + # Don't colorize things if we do not have colorama + if not colorama: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ASNI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(logging.Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level diff --git a/venv/lib/python3.5/site-packages/pip/utils/outdated.py b/venv/lib/python3.5/site-packages/pip/utils/outdated.py new file mode 100644 index 0000000..2164cc3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/outdated.py @@ -0,0 +1,162 @@ +from __future__ import absolute_import + +import datetime +import json +import logging +import os.path +import sys + +from pip._vendor import lockfile +from pip._vendor.packaging import version as packaging_version + +from pip.compat import total_seconds, WINDOWS +from pip.models import PyPI +from pip.locations import USER_CACHE_DIR, running_under_virtualenv +from pip.utils import ensure_dir, get_installed_version +from pip.utils.filesystem import check_path_owner + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +class VirtualenvSelfCheckState(object): + def __init__(self): + self.statefile_path = os.path.join(sys.prefix, "pip-selfcheck.json") + + # Load the existing state + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile) + except (IOError, ValueError): + self.state = {} + + def save(self, pypi_version, current_time): + # Attempt to write out our version check file + with open(self.statefile_path, "w") as statefile: + json.dump( + { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + }, + statefile, + sort_keys=True, + separators=(",", ":") + ) + + +class GlobalSelfCheckState(object): + def __init__(self): + self.statefile_path = os.path.join(USER_CACHE_DIR, "selfcheck.json") + + # Load the existing state + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile)[sys.prefix] + except (IOError, ValueError, KeyError): + self.state = {} + + def save(self, pypi_version, current_time): + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + # Attempt to write out our version check file + with lockfile.LockFile(self.statefile_path): + if os.path.exists(self.statefile_path): + with open(self.statefile_path) as statefile: + state = json.load(statefile) + else: + state = {} + + state[sys.prefix] = { + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + with open(self.statefile_path, "w") as statefile: + json.dump(state, statefile, sort_keys=True, + separators=(",", ":")) + + +def load_selfcheck_statefile(): + if running_under_virtualenv(): + return VirtualenvSelfCheckState() + else: + return GlobalSelfCheckState() + + +def pip_version_check(session): + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if installed_version is None: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = load_selfcheck_statefile() + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if total_seconds(current_time - last_check) < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + resp = session.get( + PyPI.pip_json_url, + headers={"Accept": "application/json"}, + ) + resp.raise_for_status() + pypi_version = [ + v for v in sorted( + list(resp.json()["releases"]), + key=packaging_version.parse, + ) + if not packaging_version.parse(v).is_prerelease + ][-1] + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + # Determine if our pypi_version is older + if (pip_version < remote_version and + pip_version.base_version != remote_version.base_version): + # Advise "python -m pip" on Windows to avoid issues + # with overwriting pip.exe. + if WINDOWS: + pip_cmd = "python -m pip" + else: + pip_cmd = "pip" + logger.warning( + "You are using pip version %s, however version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/venv/lib/python3.5/site-packages/pip/utils/setuptools_build.py b/venv/lib/python3.5/site-packages/pip/utils/setuptools_build.py new file mode 100644 index 0000000..4c9095e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/setuptools_build.py @@ -0,0 +1,6 @@ +# Shim to wrap setup.py invocation with setuptools +SETUPTOOLS_SHIM = ( + "import setuptools, tokenize;__file__=%r;" + "exec(compile(getattr(tokenize, 'open', open)(__file__).read()" + ".replace('\\r\\n', '\\n'), __file__, 'exec'))" +) diff --git a/venv/lib/python3.5/site-packages/pip/utils/ui.py b/venv/lib/python3.5/site-packages/pip/utils/ui.py new file mode 100644 index 0000000..bba73e3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/utils/ui.py @@ -0,0 +1,344 @@ +from __future__ import absolute_import +from __future__ import division + +import itertools +import sys +from signal import signal, SIGINT, default_int_handler +import time +import contextlib +import logging + +from pip.compat import WINDOWS +from pip.utils import format_size +from pip.utils.logging import get_indentation +from pip._vendor import six +from pip._vendor.progress.bar import Bar, IncrementalBar +from pip._vendor.progress.helpers import (WritelnMixin, + HIDE_CURSOR, SHOW_CURSOR) +from pip._vendor.progress.spinner import Spinner + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + +logger = logging.getLogger(__name__) + + +def _select_progress_class(preferred, fallback): + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + """ + Save the original SIGINT handler for later. + """ + super(InterruptibleMixin, self).__init__(*args, **kwargs) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + super(DownloadProgressMixin, self).__init__(*args, **kwargs) + self.message = (" " * (get_indentation() + 2)) + self.message + + @property + def downloaded(self): + return format_size(self.index) + + @property + def download_speed(self): + # Avoid zero division errors... + if self.avg == 0.0: + return "..." + return format_size(1 / self.avg) + "/s" + + @property + def pretty_eta(self): + if self.eta: + return "eta %s" % self.eta_td + return "" + + def iter(self, it, n=1): + for x in it: + yield x + self.next(n) + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call neds to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: + self.hide_cursor = False + + super(WindowsMixin, self).__init__(*args, **kwargs) + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class DownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, _BaseBar): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, WritelnMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +################################################################ +# Generic "something is happening" spinners +# +# We don't even try using progress.spinner.Spinner here because it's actually +# simpler to reimplement from scratch than to coerce their code into doing +# what we need. +################################################################ + +@contextlib.contextmanager +def hidden_cursor(file): + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 + + def ready(self): + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + self._last_update = time.time() + + +class InteractiveSpinner(object): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(object): + def __init__(self, message, min_update_interval_seconds=60): + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + if self._finished: + return + self._update("finished with status '%s'" % (final_status,)) + self._finished = True + + +@contextlib.contextmanager +def open_spinner(message): + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") diff --git a/venv/lib/python3.5/site-packages/pip/vcs/__init__.py b/venv/lib/python3.5/site-packages/pip/vcs/__init__.py new file mode 100644 index 0000000..9dc1c60 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/vcs/__init__.py @@ -0,0 +1,363 @@ +"""Handles all VCS (version control) support""" +from __future__ import absolute_import + +import errno +import logging +import os +import shutil + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip.exceptions import BadCommand +from pip.utils import (display_path, backup_dir, call_subprocess, + rmtree, ask_path_exists) + + +__all__ = ['vcs', 'get_src_requirement'] + + +logger = logging.getLogger(__name__) + + +class VcsSupport(object): + _registry = {} + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + return self._registry.__iter__() + + @property + def backends(self): + return list(self._registry.values()) + + @property + def dirnames(self): + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + schemes = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls + logger.debug('Registered VCS backend: %s', cls.name) + + def unregister(self, cls=None, name=None): + if name in self._registry: + del self._registry[name] + elif cls in self._registry.values(): + del self._registry[cls.name] + else: + logger.warning('Cannot unregister because no class or name given') + + def get_backend_name(self, location): + """ + Return the name of the version control backend if found at given + location, e.g. vcs.get_backend_name('/path/to/vcs/checkout') + """ + for vc_type in self._registry.values(): + if vc_type.controls_location(location): + logger.debug('Determine that %s uses VCS: %s', + location, vc_type.name) + return vc_type.name + return None + + def get_backend(self, name): + name = name.lower() + if name in self._registry: + return self._registry[name] + + def get_backend_from_location(self, location): + vc_type = self.get_backend_name(location) + if vc_type: + return self.get_backend(vc_type) + return None + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + # List of supported schemes for this Version Control + schemes = () + + def __init__(self, url=None, *args, **kwargs): + self.url = url + super(VersionControl, self).__init__(*args, **kwargs) + + def _is_local_repository(self, repo): + """ + posix absolute paths start with os.path.sep, + win32 ones ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or drive + + # See issue #1083 for why this method was introduced: + # https://github.com/pypa/pip/issues/1083 + def translate_egg_surname(self, surname): + # For example, Django has branches of the form "stable/1.7.x". + return surname.replace('/', '_') + + def export(self, location): + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + """ + raise NotImplementedError + + def get_url_rev(self): + """ + Returns the correct repository URL and revision by parsing the given + repository URL + """ + error_message = ( + "Sorry, '%s' is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp" + ) + assert '+' in self.url, error_message % self.url + url = self.url.split('+', 1)[1] + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev + + def get_info(self, location): + """ + Returns (url, revision), where both are strings + """ + assert not location.rstrip('/').endswith(self.dirname), \ + 'Bad directory: %s' % location + return self.get_url(location), self.get_revision(location) + + def normalize_url(self, url): + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + def compare_urls(self, url1, url2): + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (self.normalize_url(url1) == self.normalize_url(url2)) + + def obtain(self, dest): + """ + Called when installing or updating an editable package, takes the + source path of the checkout. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + """ + Switch the repo at ``dest`` to point to ``URL``. + """ + raise NotImplementedError + + def update(self, dest, rev_options): + """ + Update an already-existing repo to the given ``rev_options``. + """ + raise NotImplementedError + + def check_version(self, dest, rev_options): + """ + Return True if the version is identical to what exists and + doesn't need to be updated. + """ + raise NotImplementedError + + def check_destination(self, dest, url, rev_options, rev_display): + """ + Prepare a location to receive a checkout/clone. + + Return True if the location is ready for (and requires) a + checkout/clone, False otherwise. + """ + checkout = True + prompt = False + if os.path.exists(dest): + checkout = False + if os.path.exists(os.path.join(dest, self.dirname)): + existing_url = self.get_url(dest) + if self.compare_urls(existing_url, url): + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.check_version(dest, rev_options): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, rev_options) + else: + logger.info( + 'Skipping because already up-to-date.') + else: + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', + ('s', 'i', 'w', 'b')) + else: + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) + prompt = ('(i)gnore, (w)ipe, (b)ackup ', ('i', 'w', 'b')) + if prompt: + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) + response = ask_path_exists('What to do? %s' % prompt[0], + prompt[1]) + + if response == 's': + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + elif response == 'i': + # do nothing + pass + elif response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + checkout = True + elif response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + checkout = True + return checkout + + def unpack(self, location): + """ + Clean up current location and download the url repository + (and vcs infos) into location + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location) + + def get_src_requirement(self, dist, location): + """ + Return a string representing the requirement needed to + redownload the files currently present in location, something + like: + {repository_url}@{revision}#egg={project_name}-{version_identifier} + """ + raise NotImplementedError + + def get_url(self, location): + """ + Return the url used at location + Used in get_info or check_destination + """ + raise NotImplementedError + + def get_revision(self, location): + """ + Return the current revision of the files at location + Used in get_info + """ + raise NotImplementedError + + def run_command(self, cmd, show_stdout=True, cwd=None, + on_returncode='raise', + command_level=logging.DEBUG, command_desc=None, + extra_environ=None, spinner=None): + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = [self.name] + cmd + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode, command_level, + command_desc, extra_environ, + spinner) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand('Cannot find command %r' % self.name) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def controls_location(cls, location): + """ + Check if a location is controlled by the vcs. + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + """ + logger.debug('Checking in %s for %s (%s)...', + location, cls.dirname, cls.name) + path = os.path.join(location, cls.dirname) + return os.path.exists(path) + + +def get_src_requirement(dist, location): + version_control = vcs.get_backend_from_location(location) + if version_control: + try: + return version_control().get_src_requirement(dist, + location) + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + version_control.name, + ) + return dist.as_requirement() + logger.warning( + 'cannot determine version of editable source in %s (is not SVN ' + 'checkout, Git clone, Mercurial clone or Bazaar branch)', + location, + ) + return dist.as_requirement() diff --git a/venv/lib/python3.5/site-packages/pip/vcs/bazaar.py b/venv/lib/python3.5/site-packages/pip/vcs/bazaar.py new file mode 100644 index 0000000..0f09584 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/vcs/bazaar.py @@ -0,0 +1,116 @@ +from __future__ import absolute_import + +import logging +import os +import tempfile + +# TODO: Get this into six.moves.urllib.parse +try: + from urllib import parse as urllib_parse +except ImportError: + import urlparse as urllib_parse + +from pip.utils import rmtree, display_path +from pip.vcs import vcs, VersionControl +from pip.download import path_to_url + + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) + + def __init__(self, url=None, *args, **kwargs): + super(Bazaar, self).__init__(url, *args, **kwargs) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment or non_hierarchical + # Register lp but do not expose as a scheme to support bzr+lp. + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + urllib_parse.non_hierarchical.extend(['lp']) + + def export(self, location): + """ + Export the Bazaar repository at the url to the destination location + """ + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + if os.path.exists(location): + # Remove the location to make sure Bazaar can export it correctly + rmtree(location) + try: + self.run_command(['export', location], cwd=temp_dir, + show_stdout=False) + finally: + rmtree(temp_dir) + + def switch(self, dest, url, rev_options): + self.run_command(['switch', url], cwd=dest) + + def update(self, dest, rev_options): + self.run_command(['pull', '-q'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = ['-r', rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['branch', '-q'] + rev_options + [url, dest]) + + def get_url_rev(self): + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev = super(Bazaar, self).get_url_rev() + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev + + def get_url(self, location): + urls = self.run_command(['info'], show_stdout=False, cwd=location) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if self._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + def get_revision(self, location): + revision = self.run_command( + ['revno'], show_stdout=False, cwd=location) + return revision.splitlines()[-1] + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo: + return None + if not repo.lower().startswith('bzr:'): + repo = 'bzr+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + current_rev = self.get_revision(location) + return '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/venv/lib/python3.5/site-packages/pip/vcs/git.py b/venv/lib/python3.5/site-packages/pip/vcs/git.py new file mode 100644 index 0000000..24528de --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/vcs/git.py @@ -0,0 +1,277 @@ +from __future__ import absolute_import + +import logging +import tempfile +import os.path + +from pip.compat import samefile +from pip.exceptions import BadCommand +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip.utils import display_path, rmtree +from pip.vcs import vcs, VersionControl + + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + + def __init__(self, url=None, *args, **kwargs): + + # Works around an apparent Git bug + # (see http://article.gmane.org/gmane.comp.version-control.git/146500) + if url: + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) + url = urlunsplit((scheme, netloc, newpath, query, fragment)) + after_plus = scheme.find('+') + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + super(Git, self).__init__(url, *args, **kwargs) + + def export(self, location): + """Export the Git repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + try: + if not location.endswith('/'): + location = location + '/' + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir) + finally: + rmtree(temp_dir) + + def check_rev_options(self, rev, dest, rev_options): + """Check the revision options before checkout to compensate that tags + and branches may need origin/ as a prefix. + Returns the SHA1 of the branch or tag if found. + """ + revisions = self.get_short_refs(dest) + + origin_rev = 'origin/%s' % rev + if origin_rev in revisions: + # remote branch + return [revisions[origin_rev]] + elif rev in revisions: + # a local tag or branch name + return [revisions[rev]] + else: + logger.warning( + "Could not find a tag or branch '%s', assuming commit.", rev, + ) + return rev_options + + def check_version(self, dest, rev_options): + """ + Compare the current sha to the ref. ref may be a branch or tag name, + but current rev will always point to a sha. This means that a branch + or tag will never compare as True. So this ultimately only matches + against exact shas. + """ + return self.get_revision(dest).startswith(rev_options[0]) + + def switch(self, dest, url, rev_options): + self.run_command(['config', 'remote.origin.url', url], cwd=dest) + self.run_command(['checkout', '-q'] + rev_options, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, rev_options): + # First fetch changes from the default remote + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + if rev_options: + rev_options = self.check_rev_options( + rev_options[0], dest, rev_options, + ) + self.run_command(['reset', '--hard', '-q'] + rev_options, cwd=dest) + #: update submodules + self.update_submodules(dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = [rev] + rev_display = ' (to %s)' % rev + else: + rev_options = ['origin/master'] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Cloning %s%s to %s', url, rev_display, display_path(dest), + ) + self.run_command(['clone', '-q', url, dest]) + + if rev: + rev_options = self.check_rev_options(rev, dest, rev_options) + # Only do a checkout if rev_options differs from HEAD + if not self.check_version(dest, rev_options): + self.run_command( + ['checkout', '-q'] + rev_options, + cwd=dest, + ) + #: repo may contain submodules + self.update_submodules(dest) + + def get_url(self, location): + """Return URL of the first remote encountered.""" + remotes = self.run_command( + ['config', '--get-regexp', 'remote\..*\.url'], + show_stdout=False, cwd=location) + first_remote = remotes.splitlines()[0] + url = first_remote.split(' ')[1] + return url.strip() + + def get_revision(self, location): + current_rev = self.run_command( + ['rev-parse', 'HEAD'], show_stdout=False, cwd=location) + return current_rev.strip() + + def get_full_refs(self, location): + """Yields tuples of (commit, ref) for branches and tags""" + output = self.run_command(['show-ref'], + show_stdout=False, cwd=location) + for line in output.strip().splitlines(): + commit, ref = line.split(' ', 1) + yield commit.strip(), ref.strip() + + def is_ref_remote(self, ref): + return ref.startswith('refs/remotes/') + + def is_ref_branch(self, ref): + return ref.startswith('refs/heads/') + + def is_ref_tag(self, ref): + return ref.startswith('refs/tags/') + + def is_ref_commit(self, ref): + """A ref is a commit sha if it is not anything else""" + return not any(( + self.is_ref_remote(ref), + self.is_ref_branch(ref), + self.is_ref_tag(ref), + )) + + # Should deprecate `get_refs` since it's ambiguous + def get_refs(self, location): + return self.get_short_refs(location) + + def get_short_refs(self, location): + """Return map of named refs (branches or tags) to commit hashes.""" + rv = {} + for commit, ref in self.get_full_refs(location): + ref_name = None + if self.is_ref_remote(ref): + ref_name = ref[len('refs/remotes/'):] + elif self.is_ref_branch(ref): + ref_name = ref[len('refs/heads/'):] + elif self.is_ref_tag(ref): + ref_name = ref[len('refs/tags/'):] + if ref_name is not None: + rv[ref_name] = commit + return rv + + def _get_subdirectory(self, location): + """Return the relative path of setup.py to the git repo root.""" + # find the repo root + git_dir = self.run_command(['rev-parse', '--git-dir'], + show_stdout=False, cwd=location).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + root_dir = os.path.join(git_dir, '..') + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + # relative path of setup.py to repo root + if samefile(root_dir, location): + return None + return os.path.relpath(location, root_dir) + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('git:'): + repo = 'git+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev = self.get_revision(location) + req = '%s@%s#egg=%s' % (repo, current_rev, egg_project_name) + subdirectory = self._get_subdirectory(location) + if subdirectory: + req += '&subdirectory=' + subdirectory + return req + + def get_url_rev(self): + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes doesn't + work with a ssh:// scheme (e.g. Github). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + if '://' not in self.url: + assert 'file:' not in self.url + self.url = self.url.replace('git+', 'git+ssh://') + url, rev = super(Git, self).get_url_rev() + url = url.replace('ssh://', '') + else: + url, rev = super(Git, self).get_url_rev() + + return url, rev + + def update_submodules(self, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + self.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def controls_location(cls, location): + if super(Git, cls).controls_location(location): + return True + try: + r = cls().run_command(['rev-parse'], + cwd=location, + show_stdout=False, + on_returncode='ignore') + return not r + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return False + + +vcs.register(Git) diff --git a/venv/lib/python3.5/site-packages/pip/vcs/mercurial.py b/venv/lib/python3.5/site-packages/pip/vcs/mercurial.py new file mode 100644 index 0000000..1aa83b9 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/vcs/mercurial.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import + +import logging +import os +import tempfile + +from pip.utils import display_path, rmtree +from pip.vcs import vcs, VersionControl +from pip.download import path_to_url +from pip._vendor.six.moves import configparser + + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ('hg', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http') + + def export(self, location): + """Export the Hg repository at the url to the destination location""" + temp_dir = tempfile.mkdtemp('-export', 'pip-') + self.unpack(temp_dir) + try: + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir) + finally: + rmtree(temp_dir) + + def switch(self, dest, url, rev_options): + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.SafeConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) + else: + self.run_command(['update', '-q'] + rev_options, cwd=dest) + + def update(self, dest, rev_options): + self.run_command(['pull', '-q'], cwd=dest) + self.run_command(['update', '-q'] + rev_options, cwd=dest) + + def obtain(self, dest): + url, rev = self.get_url_rev() + if rev: + rev_options = [rev] + rev_display = ' (to revision %s)' % rev + else: + rev_options = [] + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['clone', '--noupdate', '-q', url, dest]) + self.run_command(['update', '-q'] + rev_options, cwd=dest) + + def get_url(self, location): + url = self.run_command( + ['showconfig', 'paths.default'], + show_stdout=False, cwd=location).strip() + if self._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + def get_revision(self, location): + current_revision = self.run_command( + ['parents', '--template={rev}'], + show_stdout=False, cwd=location).strip() + return current_revision + + def get_revision_hash(self, location): + current_rev_hash = self.run_command( + ['parents', '--template={node}'], + show_stdout=False, cwd=location).strip() + return current_rev_hash + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if not repo.lower().startswith('hg:'): + repo = 'hg+' + repo + egg_project_name = dist.egg_name().split('-', 1)[0] + if not repo: + return None + current_rev_hash = self.get_revision_hash(location) + return '%s@%s#egg=%s' % (repo, current_rev_hash, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + +vcs.register(Mercurial) diff --git a/venv/lib/python3.5/site-packages/pip/vcs/subversion.py b/venv/lib/python3.5/site-packages/pip/vcs/subversion.py new file mode 100644 index 0000000..aa78fa6 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/vcs/subversion.py @@ -0,0 +1,249 @@ +from __future__ import absolute_import + +import logging +import os +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip.index import Link +from pip.utils import rmtree, display_path +from pip.utils.logging import indent_log +from pip.vcs import vcs, VersionControl + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile('committed-rev="(\d+)"') +_svn_url_re = re.compile(r'URL: (.+)') +_svn_revision_re = re.compile(r'Revision: (.+)') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>') + + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + def get_info(self, location): + """Returns (url, revision), where both are strings""" + assert not location.rstrip('/').endswith(self.dirname), \ + 'Bad directory: %s' % location + output = self.run_command( + ['info', location], + show_stdout=False, + extra_environ={'LANG': 'C'}, + ) + match = _svn_url_re.search(output) + if not match: + logger.warning( + 'Cannot determine URL of svn checkout %s', + display_path(location), + ) + logger.debug('Output that cannot be parsed: \n%s', output) + return None, None + url = match.group(1).strip() + match = _svn_revision_re.search(output) + if not match: + logger.warning( + 'Cannot determine revision of svn checkout %s', + display_path(location), + ) + logger.debug('Output that cannot be parsed: \n%s', output) + return url, None + return url, match.group(1) + + def export(self, location): + """Export the svn repository at the url to the destination location""" + url, rev = self.get_url_rev() + rev_options = get_rev_options(url, rev) + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): + if os.path.exists(location): + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 + rmtree(location) + self.run_command( + ['export'] + rev_options + [url, location], + show_stdout=False) + + def switch(self, dest, url, rev_options): + self.run_command(['switch'] + rev_options + [url, dest]) + + def update(self, dest, rev_options): + self.run_command(['update'] + rev_options + [dest]) + + def obtain(self, dest): + url, rev = self.get_url_rev() + rev_options = get_rev_options(url, rev) + if rev: + rev_display = ' (to revision %s)' % rev + else: + rev_display = '' + if self.check_destination(dest, url, rev_options, rev_display): + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(['checkout', '-q'] + rev_options + [url, dest]) + + def get_location(self, dist, dependency_links): + for url in dependency_links: + egg_fragment = Link(url).egg_fragment + if not egg_fragment: + continue + if '-' in egg_fragment: + # FIXME: will this work when a package has - in the name? + key = '-'.join(egg_fragment.split('-')[:-1]).lower() + else: + key = egg_fragment + if key == dist.key: + return url.split('#', 1)[0] + return None + + def get_revision(self, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, files in os.walk(location): + if self.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(self.dirname) + entries_fn = os.path.join(base, self.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = self._get_svn_url_rev(base) + + if base == location: + base_url = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base_url): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + def get_url_rev(self): + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev = super(Subversion, self).get_url_rev() + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev + + def get_url(self, location): + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + return self._get_svn_url_rev(location)[0] + + def _get_svn_url_rev(self, location): + from pip.exceptions import InstallationError + + entries_path = os.path.join(location, self.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): + data = list(map(str.splitlines, data.split('\n\x0c\n'))) + del data[0][0] # get rid of the '8' + url = data[0][3] + revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] + elif data.startswith('<?xml'): + match = _svn_xml_url_re.search(data) + if not match: + raise ValueError('Badly formatted data: %r' % data) + url = match.group(1) # get repository URL + revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0] + else: + try: + # subversion >= 1.7 + xml = self.run_command( + ['info', '--xml', location], + show_stdout=False, + ) + url = _svn_info_xml_url_re.search(xml).group(1) + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] + except InstallationError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + def get_src_requirement(self, dist, location): + repo = self.get_url(location) + if repo is None: + return None + # FIXME: why not project name? + egg_project_name = dist.egg_name().split('-', 1)[0] + rev = self.get_revision(location) + return 'svn+%s@%s#egg=%s' % (repo, rev, egg_project_name) + + def check_version(self, dest, rev_options): + """Always assume the versions don't match""" + return False + + +def get_rev_options(url, rev): + if rev: + rev_options = ['-r', rev] + else: + rev_options = [] + + r = urllib_parse.urlsplit(url) + if hasattr(r, 'username'): + # >= Python-2.5 + username, password = r.username, r.password + else: + netloc = r[1] + if '@' in netloc: + auth = netloc.split('@')[0] + if ':' in auth: + username, password = auth.split(':', 1) + else: + username, password = auth, None + else: + username, password = None, None + + if username: + rev_options += ['--username', username] + if password: + rev_options += ['--password', password] + return rev_options + + +vcs.register(Subversion) diff --git a/venv/lib/python3.5/site-packages/pip/wheel.py b/venv/lib/python3.5/site-packages/pip/wheel.py new file mode 100644 index 0000000..1d138e6 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pip/wheel.py @@ -0,0 +1,855 @@ +""" +Support for installing and building the "wheel" binary package format. +""" +from __future__ import absolute_import + +import compileall +import csv +import errno +import functools +import hashlib +import logging +import os +import os.path +import re +import shutil +import stat +import sys +import tempfile +import warnings + +from base64 import urlsafe_b64encode +from email.parser import Parser + +from pip._vendor.six import StringIO + +import pip +from pip.compat import expanduser +from pip.download import path_to_url, unpack_url +from pip.exceptions import ( + InstallationError, InvalidWheelFilename, UnsupportedWheel) +from pip.locations import distutils_scheme, PIP_DELETE_MARKER_FILENAME +from pip import pep425tags +from pip.utils import ( + call_subprocess, ensure_dir, captured_stdout, rmtree, read_chunks, +) +from pip.utils.ui import open_spinner +from pip.utils.logging import indent_log +from pip.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six.moves import configparser + + +wheel_ext = '.whl' + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +class WheelCache(object): + """A cache of wheels for future installs.""" + + def __init__(self, cache_dir, format_control): + """Create a wheel cache. + + :param cache_dir: The root of the cache. + :param format_control: A pip.index.FormatControl object to limit + binaries being read from the cache. + """ + self._cache_dir = expanduser(cache_dir) if cache_dir else None + self._format_control = format_control + + def cached_wheel(self, link, package_name): + return cached_wheel( + self._cache_dir, link, self._format_control, package_name) + + +def _cache_for_link(cache_dir, link): + """ + Return a directory to store cached wheels in for link. + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were not + unique. E.g. ./package might have dozens of installs done for it and build + a version of 0.0...and if we built and cached a wheel, we'd end up using + the same wheel even if the source has been edited. + + :param cache_dir: The cache_dir being used by pip. + :param link: The link of the sdist for which this will cache wheels. + """ + + # We want to generate an url to use as our cache key, we don't want to just + # re-use the URL because it might have other items in the fragment and we + # don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and thus + # less secure). However the differences don't make a lot of difference for + # our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top level + # directories where we might run out of sub directories on some FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + # Inside of the base location for cached wheels, expand our parts and join + # them all together. + return os.path.join(cache_dir, "wheels", *parts) + + +def cached_wheel(cache_dir, link, format_control, package_name): + if not cache_dir: + return link + if not link: + return link + if link.is_wheel: + return link + if not link.is_artifact: + return link + if not package_name: + return link + canonical_name = canonicalize_name(package_name) + formats = pip.index.fmt_ctl_formats(format_control, canonical_name) + if "binary" not in formats: + return link + root = _cache_for_link(cache_dir, link) + try: + wheel_names = os.listdir(root) + except OSError as e: + if e.errno in (errno.ENOENT, errno.ENOTDIR): + return link + raise + candidates = [] + for wheel_name in wheel_names: + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if not wheel.supported(): + # Built for a different python/arch/etc + continue + candidates.append((wheel.support_index_min(), wheel_name)) + if not candidates: + return link + candidates.sort() + path = os.path.join(root, candidates[0][1]) + return pip.index.Link(path_to_url(path)) + + +def rehash(path, algo='sha256', blocksize=1 << 20): + """Return (hash, length) for path using hashlib.new(algo)""" + h = hashlib.new(algo) + length = 0 + with open(path, 'rb') as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + return (digest, length) + + +def open_for_csv(name, mode): + if sys.version_info[0] < 3: + nl = {} + bin = 'b' + else: + nl = {'newline': ''} + bin = '' + return open(name, mode + bin, **nl) + + +def fix_script(path): + """Replace #!python with #!/path/to/python + Return True if file was changed.""" + # XXX RECORD hashes will need to be updated + if os.path.isfile(path): + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True + +dist_info_re = re.compile(r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?) + \.dist-info$""", re.VERBOSE) + + +def root_is_purelib(name, wheeldir): + """ + Return True if the extracted wheel in wheeldir should go into purelib. + """ + name_folded = name.replace("-", "_") + for item in os.listdir(wheeldir): + match = dist_info_re.match(item) + if match and match.group('name') == name_folded: + with open(os.path.join(wheeldir, item, 'WHEEL')) as wheel: + for line in wheel: + line = line.lower().rstrip() + if line == "root-is-purelib: true": + return True + return False + + +def get_entrypoints(filename): + if not os.path.exists(filename): + return {}, {} + + # This is done because you can pass a string to entry_points wrappers which + # means that they may or may not be valid INI files. The attempt here is to + # strip leading and trailing whitespace in order to make them valid INI + # files. + with open(filename) as fp: + data = StringIO() + for line in fp: + data.write(line.strip()) + data.write("\n") + data.seek(0) + + cp = configparser.RawConfigParser() + cp.optionxform = lambda option: option + cp.readfp(data) + + console = {} + gui = {} + if cp.has_section('console_scripts'): + console = dict(cp.items('console_scripts')) + if cp.has_section('gui_scripts'): + gui = dict(cp.items('gui_scripts')) + return console, gui + + +def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None, + pycompile=True, scheme=None, isolated=False, prefix=None): + """Install a wheel""" + + if not scheme: + scheme = distutils_scheme( + name, user=user, home=home, root=root, isolated=isolated, + prefix=prefix, + ) + + if root_is_purelib(name, wheeldir): + lib_dir = scheme['purelib'] + else: + lib_dir = scheme['platlib'] + + info_dir = [] + data_dirs = [] + source = wheeldir.rstrip(os.path.sep) + os.path.sep + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} + changed = set() + generated = [] + + # Compile all of the pyc files that we're going to be installing + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + compileall.compile_dir(source, force=True, quiet=True) + logger.debug(stdout.getvalue()) + + def normpath(src, p): + return os.path.relpath(src, p).replace(os.path.sep, '/') + + def record_installed(srcfile, destfile, modified=False): + """Map archive RECORD paths to installation RECORD paths.""" + oldpath = normpath(srcfile, wheeldir) + newpath = normpath(destfile, lib_dir) + installed[oldpath] = newpath + if modified: + changed.add(destfile) + + def clobber(source, dest, is_base, fixer=None, filter=None): + ensure_dir(dest) # common for the 'include' path + + for dir, subdirs, files in os.walk(source): + basedir = dir[len(source):].lstrip(os.path.sep) + destdir = os.path.join(dest, basedir) + if is_base and basedir.split(os.path.sep, 1)[0].endswith('.data'): + continue + for s in subdirs: + destsubdir = os.path.join(dest, basedir, s) + if is_base and basedir == '' and destsubdir.endswith('.data'): + data_dirs.append(s) + continue + elif (is_base and + s.endswith('.dist-info') and + # is self.req.project_name case preserving? + s.lower().startswith( + req.name.replace('-', '_').lower())): + assert not info_dir, 'Multiple .dist-info directories' + info_dir.append(destsubdir) + for f in files: + # Skip unwanted files + if filter and filter(f): + continue + srcfile = os.path.join(dir, f) + destfile = os.path.join(dest, basedir, f) + # directory creation is lazy and after the file filtering above + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + ensure_dir(destdir) + + # We use copyfile (not move, copy, or copy2) to be extra sure + # that we are not moving directories over (copyfile fails for + # directories) as well as to ensure that we are not copying + # over any metadata because we want more control over what + # metadata we actually copy over. + shutil.copyfile(srcfile, destfile) + + # Copy over the metadata for the file, currently this only + # includes the atime and mtime. + st = os.stat(srcfile) + if hasattr(os, "utime"): + os.utime(destfile, (st.st_atime, st.st_mtime)) + + # If our file is executable, then make our destination file + # executable. + if os.access(srcfile, os.X_OK): + st = os.stat(srcfile) + permissions = ( + st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + ) + os.chmod(destfile, permissions) + + changed = False + if fixer: + changed = fixer(destfile) + record_installed(srcfile, destfile, changed) + + clobber(source, lib_dir, True) + + assert info_dir, "%s .dist-info directory not found" % req + + # Get the defined entry points + ep_file = os.path.join(info_dir[0], 'entry_points.txt') + console, gui = get_entrypoints(ep_file) + + def is_entrypoint_wrapper(name): + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + for datadir in data_dirs: + fixer = None + filter = None + for subdir in os.listdir(os.path.join(wheeldir, datadir)): + fixer = None + if subdir == 'scripts': + fixer = fix_script + filter = is_entrypoint_wrapper + source = os.path.join(wheeldir, datadir, subdir) + dest = scheme[subdir] + clobber(source, dest, False, fixer=fixer, filter=filter) + + maker = ScriptMaker(None, scheme['scripts']) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = set(('', )) + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Simplify the script and fix the fact that the default script swallows + # every single stack trace. + # See https://bitbucket.org/pypa/distlib/issue/34/ + # See https://bitbucket.org/pypa/distlib/issue/33/ + def _get_script_text(entry): + if entry.suffix is None: + raise InstallationError( + "Invalid script entry point: %s for req: %s - A callable " + "suffix is required. Cf https://packaging.python.org/en/" + "latest/distributing.html#console-scripts for more " + "information." % (entry, req) + ) + return maker.script_template % { + "module": entry.prefix, + "import_name": entry.suffix.split(".")[0], + "func": entry.suffix, + } + + maker._get_script_text = _get_script_text + maker.script_template = """# -*- coding: utf-8 -*- +import re +import sys + +from %(module)s import %(import_name)s + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(%(func)s()) +""" + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadat 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop('pip', None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'pip = ' + pip_script + generated.extend(maker.make(spec)) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + spec = 'pip%s = %s' % (sys.version[:1], pip_script) + generated.extend(maker.make(spec)) + + spec = 'pip%s = %s' % (sys.version[:3], pip_script) + generated.extend(maker.make(spec)) + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop('easy_install', None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + spec = 'easy_install = ' + easy_install_script + generated.extend(maker.make(spec)) + + spec = 'easy_install-%s = %s' % (sys.version[:3], easy_install_script) + generated.extend(maker.make(spec)) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console and GUI entry points specified in the wheel + if len(console) > 0: + generated.extend( + maker.make_multiple(['%s = %s' % kv for kv in console.items()]) + ) + if len(gui) > 0: + generated.extend( + maker.make_multiple( + ['%s = %s' % kv for kv in gui.items()], + {'gui': True} + ) + ) + + # Record pip as the installer + installer = os.path.join(info_dir[0], 'INSTALLER') + temp_installer = os.path.join(info_dir[0], 'INSTALLER.pip') + with open(temp_installer, 'wb') as installer_file: + installer_file.write(b'pip\n') + shutil.move(temp_installer, installer) + generated.append(installer) + + # Record details of all files installed + record = os.path.join(info_dir[0], 'RECORD') + temp_record = os.path.join(info_dir[0], 'RECORD.pip') + with open_for_csv(record, 'r') as record_in: + with open_for_csv(temp_record, 'w+') as record_out: + reader = csv.reader(record_in) + writer = csv.writer(record_out) + for row in reader: + row[0] = installed.pop(row[0], row[0]) + if row[0] in changed: + row[1], row[2] = rehash(row[0]) + writer.writerow(row) + for f in generated: + h, l = rehash(f) + writer.writerow((normpath(f, lib_dir), h, l)) + for f in installed: + writer.writerow((installed[f], '', '')) + shutil.move(temp_record, record) + + +def _unique(fn): + @functools.wraps(fn) + def unique(*args, **kw): + seen = set() + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +# TODO: this goes somewhere besides the wheel module +@_unique +def uninstallation_paths(dist): + """ + Yield all the uninstallation paths for dist based on RECORD-without-.pyc + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .pyc. + """ + from pip.utils import FakeFile # circular import + r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + for row in r: + path = os.path.join(dist.location, row[0]) + yield path + if path.endswith('.py'): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + '.pyc') + yield path + + +def wheel_version(source_dir): + """ + Return the Wheel-Version of an extracted wheel, if possible. + + Otherwise, return False if we couldn't parse / extract it. + """ + try: + dist = [d for d in pkg_resources.find_on_path(None, source_dir)][0] + + wheel_data = dist.get_metadata('WHEEL') + wheel_data = Parser().parsestr(wheel_data) + + version = wheel_data['Wheel-Version'].strip() + version = tuple(map(int, version.split('.'))) + return version + except: + return False + + +def check_compatibility(version, name): + """ + Raises errors or warns if called with an incompatible Wheel-Version. + + Pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if not version: + raise UnsupportedWheel( + "%s is in an unsupported or invalid wheel" % name + ) + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "%s's Wheel-Version (%s) is not compatible with this version " + "of pip" % (name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) + + +class Wheel(object): + """A wheel file""" + + # TODO: maybe move the install code into this class + + wheel_file_re = re.compile( + r"""^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?)) + ((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "%s is not a valid wheel filename." % filename + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = set( + (x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + ) + + def support_index_min(self, tags=None): + """ + Return the lowest index that one of the wheel's file_tag combinations + achieves in the supported_tags list e.g. if there are 8 supported tags, + and one of the file tags is first in the list, then return 0. Returns + None is the wheel is not supported. + """ + if tags is None: # for mock + tags = pep425tags.supported_tags + indexes = [tags.index(c) for c in self.file_tags if c in tags] + return min(indexes) if indexes else None + + def supported(self, tags=None): + """Is this wheel supported on this system?""" + if tags is None: # for mock + tags = pep425tags.supported_tags + return bool(set(tags).intersection(self.file_tags)) + + +class WheelBuilder(object): + """Build wheels from a RequirementSet.""" + + def __init__(self, requirement_set, finder, build_options=None, + global_options=None): + self.requirement_set = requirement_set + self.finder = finder + self._cache_root = requirement_set._wheel_cache._cache_dir + self._wheel_dir = requirement_set.wheel_download_dir + self.build_options = build_options or [] + self.global_options = global_options or [] + + def _build_one(self, req, output_dir, python_tag=None): + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + tempd = tempfile.mkdtemp('pip-wheel-') + try: + if self.__build_one(req, tempd, python_tag=python_tag): + try: + wheel_name = os.listdir(tempd)[0] + wheel_path = os.path.join(output_dir, wheel_name) + shutil.move(os.path.join(tempd, wheel_name), wheel_path) + logger.info('Stored in directory: %s', output_dir) + return wheel_path + except: + pass + # Ignore return, we can't do anything else useful. + self._clean_one(req) + return None + finally: + rmtree(tempd) + + def _base_setup_args(self, req): + return [ + sys.executable, "-u", '-c', + SETUPTOOLS_SHIM % req.setup_py + ] + list(self.global_options) + + def __build_one(self, req, tempd, python_tag=None): + base_args = self._base_setup_args(req) + + spin_message = 'Running setup.py bdist_wheel for %s' % (req.name,) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + wheel_args = base_args + ['bdist_wheel', '-d', tempd] \ + + self.build_options + + if python_tag is not None: + wheel_args += ["--python-tag", python_tag] + + try: + call_subprocess(wheel_args, cwd=req.setup_py_dir, + show_stdout=False, spinner=spinner) + return True + except: + spinner.finish("error") + logger.error('Failed building wheel for %s', req.name) + return False + + def _clean_one(self, req): + base_args = self._base_setup_args(req) + + logger.info('Running setup.py clean for %s', req.name) + clean_args = base_args + ['clean', '--all'] + try: + call_subprocess(clean_args, cwd=req.source_dir, show_stdout=False) + return True + except: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + def build(self, autobuilding=False): + """Build wheels. + + :param unpack: If True, replace the sdist we built from with the + newly built wheel, in preparation for installation. + :return: True if all the wheels built correctly. + """ + assert self._wheel_dir or (autobuilding and self._cache_root) + # unpack sdists and constructs req set + self.requirement_set.prepare_files(self.finder) + + reqset = self.requirement_set.requirements.values() + + buildset = [] + for req in reqset: + if req.constraint: + continue + if req.is_wheel: + if not autobuilding: + logger.info( + 'Skipping %s, due to already being wheel.', req.name) + elif req.editable: + if not autobuilding: + logger.info( + 'Skipping bdist_wheel for %s, due to being editable', + req.name) + elif autobuilding and req.link and not req.link.is_artifact: + pass + elif autobuilding and not req.source_dir: + pass + else: + if autobuilding: + link = req.link + base, ext = link.splitext() + if pip.index.egg_info_matches(base, None, link) is None: + # Doesn't look like a package - don't autobuild a wheel + # because we'll have no way to lookup the result sanely + continue + if "binary" not in pip.index.fmt_ctl_formats( + self.finder.format_control, + canonicalize_name(req.name)): + logger.info( + "Skipping bdist_wheel for %s, due to binaries " + "being disabled for it.", req.name) + continue + buildset.append(req) + + if not buildset: + return True + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join([req.name for req in buildset]), + ) + with indent_log(): + build_success, build_failure = [], [] + for req in buildset: + python_tag = None + if autobuilding: + python_tag = pep425tags.implementation_tag + output_dir = _cache_for_link(self._cache_root, req.link) + try: + ensure_dir(output_dir) + except OSError as e: + logger.warn("Building wheel for %s failed: %s", + req.name, e) + build_failure.append(req) + continue + else: + output_dir = self._wheel_dir + wheel_file = self._build_one( + req, output_dir, + python_tag=python_tag, + ) + if wheel_file: + build_success.append(req) + if autobuilding: + # XXX: This is mildly duplicative with prepare_files, + # but not close enough to pull out to a single common + # method. + # The code below assumes temporary source dirs - + # prevent it doing bad things. + if req.source_dir and not os.path.exists(os.path.join( + req.source_dir, PIP_DELETE_MARKER_FILENAME)): + raise AssertionError( + "bad source dir - missing marker") + # Delete the source we built the wheel from + req.remove_temporary_source() + # set the build directory again - name is known from + # the work prepare_files did. + req.source_dir = req.build_location( + self.requirement_set.build_dir) + # Update the link for this. + req.link = pip.index.Link( + path_to_url(wheel_file)) + assert req.link.is_wheel + # extract the wheel into the dir + unpack_url( + req.link, req.source_dir, None, False, + session=self.requirement_set.session) + else: + build_failure.append(req) + + # notify success/failure + if build_success: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_success]), + ) + if build_failure: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failure]), + ) + # Return True if all builds were successful + return len(build_failure) == 0 diff --git a/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..e118723 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,3 @@ +UNKNOWN + + diff --git a/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/METADATA b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/METADATA new file mode 100644 index 0000000..7a50487 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/METADATA @@ -0,0 +1,13 @@ +Metadata-Version: 2.0 +Name: pkg_resources +Version: 0.0.0 +Summary: UNKNOWN +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Platform: UNKNOWN + +UNKNOWN + + diff --git a/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/RECORD b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/RECORD new file mode 100644 index 0000000..3b0bcf6 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/RECORD @@ -0,0 +1,34 @@ +pkg_resources/__init__.py,sha256=5HdIlZ7QyXPbrDN2qXRsVWpMQLbeLZMgniIX5q4WKq4,101373 +pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources/_vendor/pyparsing.py,sha256=ic8qmDPiq8Li-Y0PeZcI56rEyMqevKNBK6hr6FbyVBc,160425 +pkg_resources/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pkg_resources/_vendor/packaging/__about__.py,sha256=zubjKyIqNuGdgnuwpl6C_ctMx-Zn54IhD6ejN0EZaU0,720 +pkg_resources/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +pkg_resources/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +pkg_resources/_vendor/packaging/_structures.py,sha256=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 +pkg_resources/_vendor/packaging/markers.py,sha256=eAjn0nigd-O5wN4R8Gc82OmkA9AgpQ3qBFsewqCa8yk,7573 +pkg_resources/_vendor/packaging/requirements.py,sha256=SikL2UynbsT0qtY9ltqngndha_sfo0w6XGFhAhoSoaQ,4355 +pkg_resources/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 +pkg_resources/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 +pkg_resources/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 +pkg_resources/extern/__init__.py,sha256=rMBTxKimjNg8plSH94cB-y52pKO0zmM-AkFL30lZGfY,2474 +pkg_resources-0.0.0.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 +pkg_resources-0.0.0.dist-info/METADATA,sha256=FOYDX6cmnDUkWo-yhqWQYtjKIMZR2IW2G1GFZhA6gUQ,177 +pkg_resources-0.0.0.dist-info/RECORD,, +pkg_resources-0.0.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +pkg_resources-0.0.0.dist-info/metadata.json,sha256=8ZVRFU96pY_wnWouockCkvXw981Y0iDB5nQFFGq8ZiY,221 +pkg_resources-0.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/utils.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/markers.cpython-35.pyc,, +pkg_resources/__pycache__/__init__.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-35.pyc,, +pkg_resources/_vendor/__pycache__/pyparsing.cpython-35.pyc,, +pkg_resources/_vendor/__pycache__/__init__.cpython-35.pyc,, +pkg_resources/_vendor/__pycache__/six.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/version.cpython-35.pyc,, +pkg_resources/extern/__pycache__/__init__.cpython-35.pyc,, +pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-35.pyc,, diff --git a/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/WHEEL b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/WHEEL new file mode 100644 index 0000000..8b6dd1b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/metadata.json b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/metadata.json new file mode 100644 index 0000000..f7d360a --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources-0.0.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"extensions": {"python.details": {"document_names": {"description": "DESCRIPTION.rst"}}}, "generator": "bdist_wheel (0.29.0)", "metadata_version": "2.0", "name": "pkg_resources", "summary": "UNKNOWN", "version": "0.0.0"} \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/pkg_resources/__init__.py b/venv/lib/python3.5/site-packages/pkg_resources/__init__.py new file mode 100644 index 0000000..ce4e775 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/__init__.py @@ -0,0 +1,2956 @@ +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. +""" + +from __future__ import absolute_import + +import sys +import os +import io +import time +import re +import types +import zipfile +import zipimport +import warnings +import stat +import functools +import pkgutil +import operator +import platform +import collections +import plistlib +import email.parser +import tempfile +import textwrap +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +from pkg_resources.extern import six +from pkg_resources.extern.six.moves import urllib, map, filter + +# capture these to bypass sandboxing +from os import utime +try: + from os import mkdir, rename, unlink + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +from os import open as os_open +from os.path import isdir, split + +try: + import importlib.machinery as importlib_machinery + # access attribute to force import under delayed import mechanisms. + importlib_machinery.__name__ +except ImportError: + importlib_machinery = None + +from pkg_resources.extern import packaging +__import__('pkg_resources.extern.packaging.version') +__import__('pkg_resources.extern.packaging.specifiers') +__import__('pkg_resources.extern.packaging.requirements') +__import__('pkg_resources.extern.packaging.markers') + + +if (3, 0) < sys.version_info < (3, 3): + msg = ( + "Support for Python 3.0-3.2 has been dropped. Future versions " + "will fail here." + ) + warnings.warn(msg) + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +class _SetuptoolsVersionMixin(object): + + def __hash__(self): + return super(_SetuptoolsVersionMixin, self).__hash__() + + def __lt__(self, other): + if isinstance(other, tuple): + return tuple(self) < other + else: + return super(_SetuptoolsVersionMixin, self).__lt__(other) + + def __le__(self, other): + if isinstance(other, tuple): + return tuple(self) <= other + else: + return super(_SetuptoolsVersionMixin, self).__le__(other) + + def __eq__(self, other): + if isinstance(other, tuple): + return tuple(self) == other + else: + return super(_SetuptoolsVersionMixin, self).__eq__(other) + + def __ge__(self, other): + if isinstance(other, tuple): + return tuple(self) >= other + else: + return super(_SetuptoolsVersionMixin, self).__ge__(other) + + def __gt__(self, other): + if isinstance(other, tuple): + return tuple(self) > other + else: + return super(_SetuptoolsVersionMixin, self).__gt__(other) + + def __ne__(self, other): + if isinstance(other, tuple): + return tuple(self) != other + else: + return super(_SetuptoolsVersionMixin, self).__ne__(other) + + def __getitem__(self, key): + return tuple(self)[key] + + def __iter__(self): + component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) + replace = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + }.get + + def _parse_version_parts(s): + for part in component_re.split(s): + part = replace(part, part) + if not part or part == '.': + continue + if part[:1] in '0123456789': + # pad for numeric comparison + yield part.zfill(8) + else: + yield '*'+part + + # ensure that alpha/beta/candidate are before final + yield '*final' + + def old_parse_version(s): + parts = [] + for part in _parse_version_parts(s.lower()): + if part.startswith('*'): + # remove '-' before a prerelease tag + if part < '*final': + while parts and parts[-1] == '*final-': + parts.pop() + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == '00000000': + parts.pop() + parts.append(part) + return tuple(parts) + + # Warn for use of this function + warnings.warn( + "You have iterated over the result of " + "pkg_resources.parse_version. This is a legacy behavior which is " + "inconsistent with the new version class introduced in setuptools " + "8.0. In most cases, conversion to a tuple is unnecessary. For " + "comparison of versions, sort the Version instances directly. If " + "you have another use case requiring the tuple, please file a " + "bug with the setuptools project describing that need.", + RuntimeWarning, + stacklevel=1, + ) + + for part in old_parse_version(str(self)): + yield part + + +class SetuptoolsVersion(_SetuptoolsVersionMixin, packaging.version.Version): + pass + + +class SetuptoolsLegacyVersion(_SetuptoolsVersionMixin, + packaging.version.LegacyVersion): + pass + + +def parse_version(v): + try: + return SetuptoolsVersion(v) + except packaging.version.InvalidVersion: + return SetuptoolsLegacyVersion(v) + + +_state_vars = {} + +def _declare_state(vartype, **kw): + globals().update(kw) + _state_vars.update(dict.fromkeys(kw, vartype)) + +def __getstate__(): + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_'+v](g[k]) + return state + +def __setstate__(state): + g = globals() + for k, v in state.items(): + g['_sset_'+_state_vars[k]](k, g[k], v) + return state + +def _sget_dict(val): + return val.copy() + +def _sset_dict(key, ob, state): + ob.clear() + ob.update(state) + +def _sget_object(val): + return val.__getstate__() + +def _sset_object(key, ob, state): + ob.__setstate__(state) + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of Mac OS X that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of Mac OS X that we are *running*. To allow usage of packages that + explicitly require a newer version of Mac OS X, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + except ValueError: + # not Mac OS X + pass + return plat + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', 'run_script', 'get_provider', 'get_distribution', + 'load_entry_point', 'get_entry_map', 'get_entry_info', + 'iter_entry_points', + 'resource_string', 'resource_stream', 'resource_filename', + 'resource_listdir', 'resource_exists', 'resource_isdir', + + # Environmental control + 'declare_namespace', 'working_set', 'add_activation_listener', + 'find_distributions', 'set_extraction_path', 'cleanup_resources', + 'get_default_cache', + + # Primary implementation classes + 'Environment', 'WorkingSet', 'ResourceManager', + 'Distribution', 'Requirement', 'EntryPoint', + + # Exceptions + 'ResolutionError', 'VersionConflict', 'DistributionNotFound', + 'UnknownExtra', 'ExtractionError', + + # Warnings + 'PEP440Warning', + + # Parsing functions and string utilities + 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', + 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', + 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', + + # filesystem utilities + 'ensure_directory', 'normalize_path', + + # Distribution "precedence" constants + 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', + + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', + 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', + 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', + 'register_finder', 'register_namespace_handler', 'register_loader_type', + 'fixup_namespace_packages', 'get_importer', + + # Deprecated/backward compatibility only + 'run_main', 'AvailableDistributions', +] + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + def __repr__(self): + return self.__class__.__name__+repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self): + return self.args[0] + + @property + def req(self): + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context(self, required_by): + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self): + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ("The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}") + + @property + def req(self): + return self.args[0] + + @property + def requirers(self): + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self): + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" +_provider_factories = {} + +PY_MAJOR = sys.version[:3] +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + +def register_loader_type(loader_type, provider_factory): + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + +def get_provider(moduleOrReq): + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + +def _macosx_vers(_cache=[]): + if not _cache: + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + if hasattr(plistlib, 'readPlist'): + plist_content = plistlib.readPlist(plist) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + + _cache.append(version.split('.')) + return _cache[0] + +def _macosx_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + +def get_build_platform(): + """Return this platform's string for platform-specific distributions + + XXX Currently this is the same as ``distutils.util.get_platform()``, but it + needs some hacks for Linux and Mac OS X. + """ + try: + # Python 2.7 or >=3.2 + from sysconfig import get_platform + except ImportError: + from distutils.util import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macosx_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % (int(version[0]), int(version[1]), + _macosx_arch(machine)) + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided, required): + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided==required: + # easy case + return True + + # Mac OS X special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macosx designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) + if dversion == 7 and macosversion >= "10.3" or \ + dversion == 8 and macosversion >= "10.4": + return True + # egg isn't macosx or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or \ + provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +def run_script(dist_spec, script_name): + """Locate distribution `dist_spec` and run its `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + require(dist_spec)[0].run_script(script_name, ns) + +# backward compatibility +run_main = run_script + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, six.string_types): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected string, Requirement, or Distribution", dist) + return dist + +def load_entry_point(dist, group, name): + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + +def get_entry_map(dist, group=None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + +def get_entry_info(dist, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider: + + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + + def metadata_isdir(name): + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider): + """An object that provides access to package resources""" + + def get_resource_filename(manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(manager, resource_name): + """Return a string containing the contents of `resource_name` + + `manager` must be an ``IResourceManager``""" + + def has_resource(resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(resource_name): + """List of resource names in the directory (like ``os.listdir()``)""" + + +class WorkingSet(object): + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries=None): + """Create working set from list of path entries (default=sys.path)""" + self.entries = [] + self.entry_keys = {} + self.by_key = {} + self.callbacks = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry): + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist): + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req): + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist = self.by_key.get(req.key) + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points(self, group, name=None): + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + for dist in self: + entries = dist.get_entry_map(group) + if name is None: + for ep in entries.values(): + yield ep + elif name in entries: + yield entries[name] + + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen[key]=1 + yield self.by_key[key] + + def add(self, dist, entry=None, insert=True, replace=False): + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry,[]) + keys2 = self.entry_keys.setdefault(dist.location,[]) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + def resolve(self, requirements, env=None, installer=None, + replace_conflicting=False): + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = {} + # key -> dist + best = {} + to_activate = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict(set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req): + continue + + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match(req, ws, installer) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed[req] = True + + # return list of distros to activate + return to_activate + + def find_plugins(self, plugin_env, full_env=None, installer=None, + fallback=True): + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info = {} + distributions = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + + for dist in plugin_env[project_name]: + + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + distributions = list(distributions) + distributions.sort() + + return distributions, error_info + + def require(self, *requirements): + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe(self, callback): + """Invoke `callback` for all distributions (including existing ones)""" + if callback in self.callbacks: + return + self.callbacks.append(callback) + for dist in self: + callback(dist) + + def _added_new(self, dist): + for callback in self.callbacks: + callback(dist) + + def __getstate__(self): + return ( + self.entries[:], self.entry_keys.copy(), self.by_key.copy(), + self.callbacks[:] + ) + + def __setstate__(self, e_k_b_c): + entries, keys, by_key, callbacks = e_k_b_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + extra_evals = ( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + ) + return not req.marker or any(extra_evals) or req.marker.evaluate() + + +class Environment(object): + """Searchable snapshot of distributions on a search path""" + + def __init__(self, search_path=None, platform=get_supported_platform(), + python=PY_MAJOR): + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.3'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist): + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + return (self.python is None or dist.py_version is None + or dist.py_version==self.python) \ + and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path=None): + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name): + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist): + """Add `dist` if we ``can_add()`` it and it has not already been added + """ + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + def best_match(self, req, working_set, installer=None): + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + dist = working_set.find(req) + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + def obtain(self, requirement, installer=None): + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + if installer is not None: + return installer(requirement) + + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other): + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError("Can't add %r to environment" % (other,)) + return self + + def __add__(self, other): + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + +class ResourceManager: + """Manage resource extraction and packages""" + extraction_path = None + + def __init__(self): + self.cached_files = {} + + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) + + def resource_filename(self, package_or_requirement, resource_name): + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream(self, package_or_requirement, resource_name): + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string(self, package_or_requirement, resource_name): + """Return specified resource as a string""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) + + def extraction_error(self): + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent(""" + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) to the Python egg + cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? You can + change the cache directory by setting the PYTHON_EGG_CACHE environment + variable to point to an accessible directory. + """).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name, names=()): + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name+'-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = 1 + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path): + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ("%s is writable by group/others and vulnerable to attack " + "when " + "used with get_resource_filename. Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." % path) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname, filename): + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path): + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError( + "Can't change extraction path, files already extracted" + ) + + self.extraction_path = path + + def cleanup_resources(self, force=False): + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + +def get_default_cache(): + """Determine the default cache location + + This returns the ``PYTHON_EGG_CACHE`` environment variable, if set. + Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the + "Application Data" directory. On all other systems, it's "~/.python-eggs". + """ + try: + return os.environ['PYTHON_EGG_CACHE'] + except KeyError: + pass + + if os.name!='nt': + return os.path.expanduser('~/.python-eggs') + + # XXX this may be locale-specific! + app_data = 'Application Data' + app_homes = [ + # best option, should be locale-safe + (('APPDATA',), None), + (('USERPROFILE',), app_data), + (('HOMEDRIVE','HOMEPATH'), app_data), + (('HOMEPATH',), app_data), + (('HOME',), None), + # 95/98/ME + (('WINDIR',), app_data), + ] + + for keys, subdir in app_homes: + dirname = '' + for key in keys: + if key in os.environ: + dirname = os.path.join(dirname, os.environ[key]) + else: + break + else: + if subdir: + dirname = os.path.join(dirname, subdir) + return os.path.join(dirname, 'Python-Eggs') + else: + raise RuntimeError( + "Please set the PYTHON_EGG_CACHE enviroment variable" + ) + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.]+', '_', extra).lower() + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') + + +def invalid_marker(text): + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text, extra=None): + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name = None + egg_info = None + loader = None + + def __init__(self, module): + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename(self, manager, resource_name): + return self._fn(self.module_path, resource_name) + + def get_resource_stream(self, manager, resource_name): + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string(self, manager, resource_name): + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name): + return self._has(self._fn(self.module_path, resource_name)) + + def has_metadata(self, name): + return self.egg_info and self._has(self._fn(self.egg_info, name)) + + if sys.version_info <= (3,): + def get_metadata(self, name): + if not self.egg_info: + return "" + return self._get(self._fn(self.egg_info, name)) + else: + def get_metadata(self, name): + if not self.egg_info: + return "" + return self._get(self._fn(self.egg_info, name)).decode("utf-8") + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name): + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name): + return self.egg_info and self._isdir(self._fn(self.egg_info, name)) + + def resource_listdir(self, resource_name): + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name): + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name, namespace): + script = 'scripts/'+script_name + if not self.has_metadata(script): + raise ResolutionError("No script named %r" % script_name) + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + source = open(script_filename).read() + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + cache[script_filename] = ( + len(script_text), 0, script_text.split('\n'), script_filename + ) + script_code = compile(script_text, script_filename,'exec') + exec(script_code, namespace, namespace) + + def _has(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base, resource_name): + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + def _get(self, path): + if hasattr(self.loader, 'get_data'): + return self.loader.get_data(path) + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + +register_loader_type(object, NullProvider) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + NullProvider.__init__(self, module) + self._setup_prefix() + + def _setup_prefix(self): + # we assume here that our metadata may be nested inside a "basket" + # of multiple eggs; that's why we use module_path instead of .archive + path = self.module_path + old = None + while path!=old: + if _is_unpacked_egg(path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + break + old = path + path, base = os.path.split(path) + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path): + return os.path.exists(path) + + def _isdir(self, path): + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream(self, manager, resource_name): + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path): + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls): + loader_cls = getattr(importlib_machinery, 'SourceFileLoader', + type(None)) + register_loader_type(loader_cls, cls) + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + _isdir = _has = lambda self, path: False + _get = lambda self, path: '' + _listdir = lambda self, path: [] + module_path = None + + def __init__(self): + pass + +empty_provider = EmptyProvider() + + +class ZipManifests(dict): + """ + zip manifest builder + """ + + @classmethod + def build(cls, path): + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with ContextualZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') + + def load(self, path): + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ContextualZipFile(zipfile.ZipFile): + """ + Supplement ZipFile class to support context manager for Python 2.6 + """ + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def __new__(cls, *args, **kwargs): + """ + Construct a ZipFile or ContextualZipFile as appropriate + """ + if hasattr(zipfile.ZipFile, '__exit__'): + return zipfile.ZipFile(*args, **kwargs) + return super(ContextualZipFile, cls).__new__(cls) + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers = None + _zip_manifests = MemoizedZipManifests() + + def __init__(self, module): + EggProvider.__init__(self, module) + self.zip_pre = self.loader.archive+os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre):] + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.zip_pre) + ) + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre+zip_path + if fspath.startswith(self.egg_root+os.sep): + return fspath[len(self.egg_root)+1:].split(os.sep) + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.egg_root) + ) + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename(self, manager, resource_name): + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + def _extract_resource(self, manager, zip_path): + + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource( + manager, os.path.join(zip_path, name) + ) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise IOError('"os.rename" and "os.unlink" are not supported ' + 'on this platform') + try: + + real_path = manager.get_cache_path( + self.egg_name, self._parts(zip_path) + ) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path)) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except os.error: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name=='nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except os.error: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size!=size or stat.st_mtime!=timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath): + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath): + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path): + self.path = path + + def has_metadata(self, name): + return name=='PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name): + if name=='PKG-INFO': + with io.open(self.path, encoding='utf-8') as f: + try: + metadata = f.read() + except UnicodeDecodeError as exc: + # add path context to error message + tmpl = " in {self.path}" + exc.reason += tmpl.format(self=self) + raise + return metadata + raise KeyError("No metadata except PKG-INFO is available") + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path, egg_info): + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer): + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive+os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + +_declare_state('dict', _distribution_finders = {}) + +def register_finder(importer_type, distribution_finder): + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item, only=False): + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + +def find_eggs_in_zip(importer, path_item, only=False): + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir('/'): + if _is_unpacked_egg(subitem): + subpath = os.path.join(path_item, subitem) + for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath): + yield dist + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + +def find_nothing(importer, path_item, only=False): + return () +register_finder(object, find_nothing) + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if os.path.isdir(path_item) and os.access(path_item, os.R_OK): + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item,'EGG-INFO') + ) + ) + else: + # scan for .egg and .egg-info in directory + for entry in os.listdir(path_item): + lower = entry.lower() + if lower.endswith('.egg-info') or lower.endswith('.dist-info'): + fullpath = os.path.join(path_item, entry) + if os.path.isdir(fullpath): + # egg-info directory, allow getting metadata + metadata = PathMetadata(path_item, fullpath) + else: + metadata = FileMetadata(fullpath) + yield Distribution.from_location( + path_item, entry, metadata, precedence=DEVELOP_DIST + ) + elif not only and _is_unpacked_egg(entry): + dists = find_distributions(os.path.join(path_item, entry)) + for dist in dists: + yield dist + elif not only and lower.endswith('.egg-link'): + with open(os.path.join(path_item, entry)) as entry_file: + entry_lines = entry_file.readlines() + for line in entry_lines: + if not line.strip(): + continue + path = os.path.join(path_item, line.rstrip()) + dists = find_distributions(path) + for item in dists: + yield item + break +register_finder(pkgutil.ImpImporter, find_on_path) + +if hasattr(importlib_machinery, 'FileFinder'): + register_finder(importlib_machinery.FileFinder, find_on_path) + +_declare_state('dict', _namespace_handlers={}) +_declare_state('dict', _namespace_packages={}) + + +def register_namespace_handler(importer_type, namespace_handler): + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + loader = importer.find_module(packageName) + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module,'__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + loader.load_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module): + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return sys_path.index(_normalize_cached(os.sep.join(parts))) + + orig_path.sort(key=position_in_sys_path) + module.__path__[:] = [_normalize_cached(p) for p in orig_path] + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path, parent = sys.path, None + if '.' in packageName: + parent = '.'.join(packageName.split('.')[:-1]) + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError: + raise TypeError("Not a package:", parent) + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent,[]).append(packageName) + _namespace_packages.setdefault(packageName,[]) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + +def fixup_namespace_packages(path_item, parent=None): + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent,()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + +def file_ns_handler(importer, path_item, packageName, module): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item)==normalized: + break + else: + # Only return the path if it's not already there + return subpath + +register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) +register_namespace_handler(zipimport.zipimporter, file_ns_handler) + +if hasattr(importlib_machinery, 'FileFinder'): + register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + + +def null_ns_handler(importer, path_item, packageName, module): + return None + +register_namespace_handler(object, null_ns_handler) + + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(filename)) + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + path.lower().endswith('.egg') + ) + +def _set_parent_ns(packageName): + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +def yield_lines(strs): + """Yield non-empty/non-comment lines of a string or sequence""" + if isinstance(strs, six.string_types): + for s in strs.splitlines(): + s = s.strip() + # skip blank lines/comments + if s and not s.startswith('#'): + yield s + else: + for ss in strs: + for s in yield_lines(ss): + yield s + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P<name>[^-]+) ( + -(?P<ver>[^-]+) ( + -py(?P<pyver>[^-]+) ( + -(?P<plat>.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint(object): + """Object representing an advertised importable object""" + + def __init__(self, name, module_name, attrs=(), extras=(), dist=None): + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = Requirement.parse(("x[%s]" % ','.join(extras))).extras + self.dist = dist + + def __str__(self): + s = "%s = %s" % (self.name, self.module_name) + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + s += ' [%s]' % ','.join(self.extras) + return s + + def __repr__(self): + return "EntryPoint.parse(%r)" % str(self) + + def load(self, require=True, *args, **kwargs): + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + DeprecationWarning, + stacklevel=2, + ) + if require: + self.require(*args, **kwargs) + return self.resolve() + + def resolve(self): + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) + + def require(self, env=None, installer=None): + if self.extras and not self.dist: + raise UnknownExtra("Can't require() without a distribution", self) + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P<name>.+?)\s*' + r'=\s*' + r'(?P<module>[\w.]+)\s*' + r'(:\s*(?P<attr>[\w.]+))?\s*' + r'(?P<extras>\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src, dist=None): + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError() + return req.extras + + @classmethod + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name]=ep + return this + + @classmethod + def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" + if isinstance(data, dict): + data = data.items() + else: + data = split_sections(data) + maps = {} + for group, lines in data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _remove_md5_fragment(location): + if not location: + return '' + parsed = urllib.parse.urlparse(location) + if parsed[-1].startswith('md5='): + return urllib.parse.urlunparse(parsed[:-1] + ('',)) + return location + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + is_version_line = lambda line: line.lower().startswith('version:') + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution(object): + """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' + + def __init__(self, location=None, metadata=None, project_name=None, + version=None, py_version=PY_MAJOR, platform=None, + precedence=EGG_DIST): + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location(cls, location, basename, metadata=None, **kw): + project_name, version, py_version, platform = [None]*4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, metadata, project_name=project_name, version=version, + py_version=py_version, platform=platform, **kw + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self.parsed_version, + self.precedence, + self.key, + _remove_md5_fragment(self.location), + self.py_version or '', + self.platform or '', + ) + + def __hash__(self): + return hash(self.hashcmp) + + def __lt__(self, other): + return self.hashcmp < other.hashcmp + + def __le__(self, other): + return self.hashcmp <= other.hashcmp + + def __gt__(self, other): + return self.hashcmp > other.hashcmp + + def __ge__(self, other): + return self.hashcmp >= other.hashcmp + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other): + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + self._parsed_version = parse_version(self.version) + + return self._parsed_version + + def _warn_legacy_version(self): + LV = packaging.version.LegacyVersion + is_legacy = isinstance(self._parsed_version, LV) + if not is_legacy: + return + + # While an empty version is technically a legacy version and + # is not a valid PEP 440 version, it's also unlikely to + # actually come from someone and instead it is more likely that + # it comes from setuptools attempting to parse a filename and + # including it in the list. So for that we'll gate this warning + # on if the version is anything at all or not. + if not self.version: + return + + tmpl = textwrap.dedent(""" + '{project_name} ({version})' is being parsed as a legacy, + non PEP 440, + version. You may find odd behavior and sort order. + In particular it will be sorted as less than 0.0. It + is recommended to migrate to PEP 440 compatible + versions. + """).strip().replace('\n', ' ') + + warnings.warn(tmpl.format(**vars(self)), PEP440Warning) + + @property + def version(self): + try: + return self._version + except AttributeError: + version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if version is None: + tmpl = "Missing 'Version:' header and/or %s file" + raise ValueError(tmpl % self.PKG_INFO, self) + return version + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + dm = self.__dep_map = {None: []} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + if extra: + if ':' in extra: + extra, marker = extra.split(':', 1) + if invalid_marker(marker): + # XXX warn + reqs=[] + elif not evaluate_marker(marker): + reqs=[] + extra = safe_extra(extra) or None + dm.setdefault(extra,[]).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras=()): + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) + return deps + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def activate(self, path=None): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=True) + if path is sys.path: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = "%s-%s-py%s" % ( + to_filename(self.project_name), to_filename(self.version), + self.py_version or PY_MAJOR + ) + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self): + if self.location: + return "%s (%s)" % (self, self.location) + else: + return str(self) + + def __str__(self): + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return "%s %s" % (self.project_name, version) + + def __getattr__(self, attr): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + @classmethod + def from_filename(cls, filename, metadata=None, **kw): + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, + **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = "%s==%s" % (self.project_name, self.parsed_version) + else: + spec = "%s===%s" % (self.project_name, self.parsed_version) + + return Requirement.parse(spec) + + def load_entry_point(self, group, name): + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError("Entry point %r not found" % ((group, name),)) + return ep.load() + + def get_entry_map(self, group=None): + """Return the entry point map for `group`, or the full entry map""" + try: + ep_map = self._ep_map + except AttributeError: + ep_map = self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return ep_map.get(group,{}) + return ep_map + + def get_entry_info(self, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + def insert_on(self, path, loc=None, replace=False): + """Insert self.location in path before its nearest parent directory""" + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath= [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + break + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p+1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if (modname not in sys.modules or modname in nsp + or modname in _namespace_packages): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and (normalize_path(fn).startswith(loc) or + fn.startswith(self.location)): + continue + issue_warning( + "Module %s was already imported from %s, but %s is being added" + " to sys.path" % (modname, fn, self.location), + ) + + def has_version(self): + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + return True + + def clone(self,**kw): + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + return self.__class__(**kw) + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = _version_from_file(self._get_metadata(self.PKG_INFO)) + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """Wrap an actual or potential sys.path entry w/metadata, .dist-info style""" + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = frozenset(reqs_for_extra(None)) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + extra = safe_extra(extra.strip()) + dm[extra] = list(frozenset(reqs_for_extra(extra)) - common) + + return dm + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, + } + + +def issue_warning(*args,**kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +class RequirementParseError(ValueError): + def __str__(self): + return ' '.join(self.args) + + +def parse_requirements(strs): + """Yield ``Requirement`` objects for each specification in `strs` + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + # create a steppable iterator, so we can handle \-continuations + lines = iter(yield_lines(strs)) + + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if ' #' in line: + line = line[:line.find(' #')] + # If there is a line continuation, drop it, and append the next line. + if line.endswith('\\'): + line = line[:-2].strip() + line += next(lines) + yield Requirement(line) + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + try: + super(Requirement, self).__init__(requirement_string) + except packaging.requirements.InvalidRequirement as e: + raise RequirementParseError(str(e)) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [ + (spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other): + return ( + isinstance(other, Requirement) and + self.hashCmp == other.hashCmp + ) + + def __ne__(self, other): + return not self == other + + def __contains__(self, item): + if isinstance(item, Distribution): + if item.key != self.key: + return False + + item = item.version + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains(item, prereleases=True) + + def __hash__(self): + return self.__hash + + def __repr__(self): return "Requirement.parse(%r)" % str(self) + + @staticmethod + def parse(s): + req, = parse_requirements(s) + return req + + +def _get_mro(cls): + """Get an mro for a type or classic class""" + if not isinstance(cls, type): + class cls(cls, object): pass + return cls.__mro__[1:] + return cls.__mro__ + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + for t in _get_mro(getattr(ob, '__class__', type(ob))): + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise IOError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + mkdir(dirname, 0o755) + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + +def _mkstemp(*args,**kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args,**kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()): + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + for name in dir(manager): + if not name.startswith('_'): + g[name] = getattr(manager, name) + + +@_call_aside +def _initialize_master_working_set(): + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = WorkingSet._build_master() + _declare_state('object', working_set=working_set) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path, and ensure that + # all distributions added to the working set in the future (e.g. by + # calling ``require()``) will get activated as well. + add_activation_listener(lambda dist: dist.activate()) + working_set.entries=[] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/__init__.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__about__.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__about__.py new file mode 100644 index 0000000..baa9075 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__about__.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "16.6" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2016 %s" % __author__ diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__init__.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_compat.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_compat.py new file mode 100644 index 0000000..210bb80 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_compat.py @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = str, +else: + string_types = basestring, + + +def with_metaclass(meta, *bases): + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_structures.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_structures.py new file mode 100644 index 0000000..ccc2786 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/_structures.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class Infinity(object): + + def __repr__(self): + return "Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return True + + def __ge__(self, other): + return True + + def __neg__(self): + return NegativeInfinity + +Infinity = Infinity() + + +class NegativeInfinity(object): + + def __repr__(self): + return "-Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return True + + def __le__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return False + + def __ge__(self, other): + return False + + def __neg__(self): + return Infinity + +NegativeInfinity = NegativeInfinity() diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/markers.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/markers.py new file mode 100644 index 0000000..bac852d --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -0,0 +1,278 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from pkg_resources.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pkg_resources.extern.pyparsing import Literal as L # noqa + +from ._compat import string_types +from .specifiers import Specifier, InvalidSpecifier + + +__all__ = [ + "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", + "Marker", "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + def __repr__(self): + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + +class Variable(Node): + pass + + +class Value(Node): + pass + + +VARIABLE = ( + L("implementation_version") | + L("platform_python_implementation") | + L("implementation_name") | + L("python_full_version") | + L("platform_release") | + L("platform_version") | + L("platform_machine") | + L("platform_system") | + L("python_version") | + L("sys_platform") | + L("os_name") | + L("os.name") | # PEP-345 + L("sys.platform") | # PEP-345 + L("platform.version") | # PEP-345 + L("platform.machine") | # PEP-345 + L("platform.python_implementation") | # PEP-345 + L("extra") +) +VARIABLE.setParseAction(lambda s, l, t: Variable(t[0].replace('.', '_'))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if (isinstance(marker, list) and len(marker) == 1 and + isinstance(marker[0], (list, tuple))): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return '{0} {1} "{2}"'.format(*marker) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs, op, rhs): + try: + spec = Specifier("".join([op, rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op) + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +_undefined = object() + + +def _get_env(environment, name): + value = environment.get(name, _undefined) + + if value is _undefined: + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + groups = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + version = '{0.major}.{0.minor}.{0.micro}'.format(info) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + if hasattr(sys, 'implementation'): + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + iver = '0' + implementation_name = '' + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": platform.python_version()[:3], + "sys_platform": sys.platform, + } + + +class Marker(object): + + def __init__(self, marker): + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc:e.loc + 8]) + raise InvalidMarker(err_str) + + def __str__(self): + return _format_marker(self._markers) + + def __repr__(self): + return "<Marker({0!r})>".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/requirements.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/requirements.py new file mode 100644 index 0000000..0c8c4a3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/requirements.py @@ -0,0 +1,127 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pkg_resources.extern.pyparsing import Literal as L # noqa +from pkg_resources.extern.six.moves.urllib import parse as urlparse + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r'[^ ]+')("url") +URL = (AT + URI) + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), + joinString=",", adjacent=False)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start:t._original_end]) +) +MARKER_SEPERATOR = SEMICOLON +MARKER = MARKER_SEPERATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = \ + NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + "Invalid requirement, parse error at \"{0!r}\"".format( + requirement_string[e.loc:e.loc + 8])) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc): + raise InvalidRequirement("Invalid URL given") + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + parts = [self.name] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + return "<Requirement({0!r})>".format(str(self)) diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..7f5a76c --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/specifiers.py @@ -0,0 +1,774 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from .version import Version, LegacyVersion, parse + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): + + @abc.abstractmethod + def __str__(self): + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} + + def __init__(self, spec="", prereleases=None): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format( + self.__class__.__name__, + str(self), + pre, + ) + + def __str__(self): + return "{0}{1}".format(*self._spec) + + def __hash__(self): + return hash(self._spec) + + def __eq__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + return getattr(self, "_compare_{0}".format(self._operators[op])) + + def _coerce_version(self, version): + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + return self._spec[0] + + @property + def version(self): + return self._spec[1] + + @property + def prereleases(self): + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + return self._get_operator(self.operator)(item, self.version) + + def filter(self, iterable, prereleases=None): + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if (parsed_version.is_prerelease and not + (prereleases or self.prereleases)): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the begining. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(==|!=|<=|>=|<|>)) + \s* + (?P<version> + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + return prospective > self._coerce_version(spec) + + +def _require_version_compare(fn): + @functools.wraps(fn) + def wrapped(self, prospective, spec): + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P<operator>(~=|==|!=|<=|>=|<|>|===)) + (?P<version> + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?<!==|!=|~=) # We have special cases for these + # operators so we want to make sure they + # don't match here. + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "~=": "compatible", + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not + x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return (self._get_operator(">=")(prospective, spec) and + self._get_operator("==")(prospective, prefix)) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + prospective = prospective[:len(spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + spec, prospective = _pad_version(spec, prospective) + else: + # Convert our spec string into a Version + spec = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec.local: + prospective = Version(prospective.public) + + return prospective == spec + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is techincally greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + result = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]):]) + right_split.append(right[len(right_split[0]):]) + + # Insert our padding + left_split.insert( + 1, + ["0"] * max(0, len(right_split[0]) - len(left_split[0])), + ) + right_split.insert( + 1, + ["0"] * max(0, len(left_split[0]) - len(right_split[0])), + ) + + return ( + list(itertools.chain(*left_split)), + list(itertools.chain(*right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + + def __init__(self, specifiers="", prereleases=None): + # Split on , to break each indidivual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<SpecifierSet({0!r}{1})>".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all( + s.contains(item, prereleases=prereleases) + for s in self._specs + ) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/utils.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/utils.py new file mode 100644 index 0000000..942387c --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/version.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/version.py new file mode 100644 index 0000000..83b5ee8 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/packaging/version.py @@ -0,0 +1,393 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity + + +__all__ = [ + "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" +] + + +_Version = collections.namedtuple( + "_Version", + ["epoch", "release", "dev", "pre", "post", "local"], +) + + +def parse(version): + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + + def __init__(self, version): + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + return self._version + + def __repr__(self): + return "<LegacyVersion({0})>".format(repr(str(self))) + + @property + def public(self): + return self._version + + @property + def base_version(self): + return self._version + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + +_legacy_version_component_re = re.compile( + r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, +) + +_legacy_version_replacement_map = { + "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + parts = tuple(parts) + + return epoch, parts + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P<epoch>[0-9]+)!)? # epoch + (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment + (?P<pre> # pre-release + [-_\.]? + (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview)) + [-_\.]? + (?P<pre_n>[0-9]+)? + )? + (?P<post> # post release + (?:-(?P<post_n1>[0-9]+)) + | + (?: + [-_\.]? + (?P<post_l>post|rev|r) + [-_\.]? + (?P<post_n2>[0-9]+)? + ) + )? + (?P<dev> # dev release + [-_\.]? + (?P<dev_l>dev) + [-_\.]? + (?P<dev_n>[0-9]+)? + )? + ) + (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version +""" + + +class Version(_BaseVersion): + + _regex = re.compile( + r"^\s*" + VERSION_PATTERN + r"\s*$", + re.VERBOSE | re.IGNORECASE, + ) + + def __init__(self, version): + # Validate the version and parse it into pieces + match = self._regex.search(version) + if not match: + raise InvalidVersion("Invalid version: '{0}'".format(version)) + + # Store the parsed out pieces of the version + self._version = _Version( + epoch=int(match.group("epoch")) if match.group("epoch") else 0, + release=tuple(int(i) for i in match.group("release").split(".")), + pre=_parse_letter_version( + match.group("pre_l"), + match.group("pre_n"), + ), + post=_parse_letter_version( + match.group("post_l"), + match.group("post_n1") or match.group("post_n2"), + ), + dev=_parse_letter_version( + match.group("dev_l"), + match.group("dev_n"), + ), + local=_parse_local_version(match.group("local")), + ) + + # Generate a key which will be used for sorting + self._key = _cmpkey( + self._version.epoch, + self._version.release, + self._version.pre, + self._version.post, + self._version.dev, + self._version.local, + ) + + def __repr__(self): + return "<Version({0})>".format(repr(str(self))) + + def __str__(self): + parts = [] + + # Epoch + if self._version.epoch != 0: + parts.append("{0}!".format(self._version.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self._version.release)) + + # Pre-release + if self._version.pre is not None: + parts.append("".join(str(x) for x in self._version.pre)) + + # Post-release + if self._version.post is not None: + parts.append(".post{0}".format(self._version.post[1])) + + # Development release + if self._version.dev is not None: + parts.append(".dev{0}".format(self._version.dev[1])) + + # Local version segment + if self._version.local is not None: + parts.append( + "+{0}".format(".".join(str(x) for x in self._version.local)) + ) + + return "".join(parts) + + @property + def public(self): + return str(self).split("+", 1)[0] + + @property + def base_version(self): + parts = [] + + # Epoch + if self._version.epoch != 0: + parts.append("{0}!".format(self._version.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self._version.release)) + + return "".join(parts) + + @property + def local(self): + version_string = str(self) + if "+" in version_string: + return version_string.split("+", 1)[1] + + @property + def is_prerelease(self): + return bool(self._version.dev or self._version.pre) + + @property + def is_postrelease(self): + return bool(self._version.post) + + +def _parse_letter_version(letter, number): + if letter: + # We consider there to be an implicit 0 in a pre-release if there is + # not a numeral associated with it. + if number is None: + number = 0 + + # We normalize any letters to their lower case form + letter = letter.lower() + + # We consider some words to be alternate spellings of other words and + # in those cases we want to normalize the spellings to our preferred + # spelling. + if letter == "alpha": + letter = "a" + elif letter == "beta": + letter = "b" + elif letter in ["c", "pre", "preview"]: + letter = "rc" + elif letter in ["rev", "r"]: + letter = "post" + + return letter, int(number) + if not letter and number: + # We assume if we are given a number, but we are not given a letter + # then this is using the implicit post release syntax (e.g. 1.0-1) + letter = "post" + + return letter, int(number) + + +_local_version_seperators = re.compile(r"[\._-]") + + +def _parse_local_version(local): + """ + Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). + """ + if local is not None: + return tuple( + part.lower() if not part.isdigit() else int(part) + for part in _local_version_seperators.split(local) + ) + + +def _cmpkey(epoch, release, pre, post, dev, local): + # When we compare a release version, we want to compare it with all of the + # trailing zeros removed. So we'll use a reverse the list, drop all the now + # leading zeros until we come to something non zero, then take the rest + # re-reverse it back into the correct order and make it a tuple and use + # that for our sorting key. + release = tuple( + reversed(list( + itertools.dropwhile( + lambda x: x == 0, + reversed(release), + ) + )) + ) + + # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. + # We'll do this by abusing the pre segment, but we _only_ want to do this + # if there is not a pre or a post segment. If we have one of those then + # the normal sorting rules will handle this case correctly. + if pre is None and post is None and dev is not None: + pre = -Infinity + # Versions without a pre-release (except as noted above) should sort after + # those with one. + elif pre is None: + pre = Infinity + + # Versions without a post segment should sort before those with one. + if post is None: + post = -Infinity + + # Versions without a development segment should sort after those with one. + if dev is None: + dev = Infinity + + if local is None: + # Versions without a local segment should sort before those with one. + local = -Infinity + else: + # Versions with a local segment need that segment parsed to implement + # the sorting rules in PEP440. + # - Alpha numeric segments sort before numeric segments + # - Alpha numeric segments sort lexicographically + # - Numeric segments sort numerically + # - Shorter versions sort before longer versions when the prefixes + # match exactly + local = tuple( + (i, "") if isinstance(i, int) else (-Infinity, i) + for i in local + ) + + return epoch, release, pre, post, dev, local diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/pyparsing.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/pyparsing.py new file mode 100644 index 0000000..3e02dbe --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/pyparsing.py @@ -0,0 +1,3805 @@ +# module pyparsing.py +# +# Copyright (c) 2003-2015 Paul T. McGuire +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__doc__ = \ +""" +pyparsing module - Classes and methods to define and execute parsing grammars + +The pyparsing module is an alternative approach to creating and executing simple grammars, +vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you +don't need to learn a new syntax for defining grammars or matching expressions - the parsing module +provides a library of classes that you use to construct the grammar directly in Python. + +Here is a program to parse "Hello, World!" (or any greeting of the form C{"<salutation>, <addressee>!"}):: + + from pyparsing import Word, alphas + + # define grammar of a greeting + greet = Word( alphas ) + "," + Word( alphas ) + "!" + + hello = "Hello, World!" + print (hello, "->", greet.parseString( hello )) + +The program outputs the following:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + +The Python representation of the grammar is quite readable, owing to the self-explanatory +class names, and the use of '+', '|' and '^' operators. + +The parsed results returned from C{parseString()} can be accessed as a nested list, a dictionary, or an +object with named attributes. + +The pyparsing module handles some of the problems that are typically vexing when writing text parsers: + - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.) + - quoted strings + - embedded comments +""" + +__version__ = "2.0.6" +__versionTime__ = "9 Nov 2015 19:03" +__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>" + +import string +from weakref import ref as wkref +import copy +import sys +import warnings +import re +import sre_constants +import collections +import pprint +import functools +import itertools + +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', +'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', +'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', +'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', +'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', +'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', +'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', +'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', +] + +PY_3 = sys.version.startswith('3') +if PY_3: + _MAX_INT = sys.maxsize + basestring = str + unichr = chr + _ustr = str + + # build list of single arg builtins, that can be used as parse actions + singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max] + +else: + _MAX_INT = sys.maxint + range = xrange + + def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj + + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) + + except UnicodeEncodeError: + # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) + # state that "The return value must be a string object". However, does a + # unicode object (being a subclass of basestring) count as a "string + # object"? + # If so, then return a unicode object: + return unicode(obj) + # Else encode it... but how? There are many choices... :) + # Replace unprintables with escape codes? + #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') + # Replace unprintables with question marks? + #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') + # ... + + # build list of single arg builtins, tolerant of Python version, that can be used as parse actions + singleArgBuiltins = [] + import __builtin__ + for fname in "sum len sorted reversed list tuple set any all min max".split(): + try: + singleArgBuiltins.append(getattr(__builtin__,fname)) + except AttributeError: + continue + +_generatorType = type((y for y in range(1))) + +def _xml_escape(data): + """Escape &, <, >, ", ', etc. in a string of data.""" + + # ampersand must be replaced first + from_symbols = '&><"\'' + to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split()) + for from_,to_ in zip(from_symbols, to_symbols): + data = data.replace(from_, to_) + return data + +class _Constants(object): + pass + +alphas = string.ascii_lowercase + string.ascii_uppercase +nums = "0123456789" +hexnums = nums + "ABCDEFabcdef" +alphanums = alphas + nums +_bslash = chr(92) +printables = "".join(c for c in string.printable if c not in string.whitespace) + +class ParseBaseException(Exception): + """base exception class for all parsing runtime exceptions""" + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, pstr, loc=0, msg=None, elem=None ): + self.loc = loc + if msg is None: + self.msg = pstr + self.pstr = "" + else: + self.msg = msg + self.pstr = pstr + self.parserElement = elem + + def __getattr__( self, aname ): + """supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + if( aname == "lineno" ): + return lineno( self.loc, self.pstr ) + elif( aname in ("col", "column") ): + return col( self.loc, self.pstr ) + elif( aname == "line" ): + return line( self.loc, self.pstr ) + else: + raise AttributeError(aname) + + def __str__( self ): + return "%s (at char %d), (line:%d, col:%d)" % \ + ( self.msg, self.loc, self.lineno, self.column ) + def __repr__( self ): + return _ustr(self) + def markInputline( self, markerString = ">!<" ): + """Extracts the exception line from the input string, and marks + the location of the exception with a special symbol. + """ + line_str = self.line + line_column = self.column - 1 + if markerString: + line_str = "".join((line_str[:line_column], + markerString, line_str[line_column:])) + return line_str.strip() + def __dir__(self): + return "loc msg pstr parserElement lineno col line " \ + "markInputline __str__ __repr__".split() + +class ParseException(ParseBaseException): + """exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like C{L{ParseFatalException}}, but thrown internally when an + C{L{ErrorStop<And._ErrorStop>}} ('-' operator) indicates that parsing is to stop immediately because + an unbacktrackable syntax error has been found""" + def __init__(self, pe): + super(ParseSyntaxException, self).__init__( + pe.pstr, pe.loc, pe.msg, pe.parserElement) + +#~ class ReparseException(ParseBaseException): + #~ """Experimental class - parse actions can raise this exception to cause + #~ pyparsing to reparse the input string: + #~ - with a modified input string, and/or + #~ - with a modified start location + #~ Set the values of the ReparseException in the constructor, and raise the + #~ exception in a parse action to cause pyparsing to use the new string/location. + #~ Setting the values as None causes no change to be made. + #~ """ + #~ def __init_( self, newstring, restartLoc ): + #~ self.newParseText = newstring + #~ self.reparseLoc = restartLoc + +class RecursiveGrammarException(Exception): + """exception thrown by C{validate()} if the grammar could be improperly recursive""" + def __init__( self, parseElementList ): + self.parseElementTrace = parseElementList + + def __str__( self ): + return "RecursiveGrammarException: %s" % self.parseElementTrace + +class _ParseResultsWithOffset(object): + def __init__(self,p1,p2): + self.tup = (p1,p2) + def __getitem__(self,i): + return self.tup[i] + def __repr__(self): + return repr(self.tup) + def setOffset(self,i): + self.tup = (self.tup[0],i) + +class ParseResults(object): + """Structured parse results, to provide multiple means of access to the parsed data: + - as a list (C{len(results)}) + - by list index (C{results[0], results[1]}, etc.) + - by attribute (C{results.<resultsName>}) + """ + def __new__(cls, toklist, name=None, asList=True, modal=True ): + if isinstance(toklist, cls): + return toklist + retobj = object.__new__(cls) + retobj.__doinit = True + return retobj + + # Performance tuning: we construct a *lot* of these, so keep this + # constructor as small and fast as possible + def __init__( self, toklist, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + if isinstance(toklist, list): + self.__toklist = toklist[:] + elif isinstance(toklist, _generatorType): + self.__toklist = list(toklist) + else: + self.__toklist = [toklist] + self.__tokdict = dict() + + if name is not None and name: + if not modal: + self.__accumNames[name] = 0 + if isinstance(name,int): + name = _ustr(name) # will always return a str, but use _ustr for consistency + self.__name = name + if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])): + if isinstance(toklist,basestring): + toklist = [ toklist ] + if asList: + if isinstance(toklist,ParseResults): + self[name] = _ParseResultsWithOffset(toklist.copy(),0) + else: + self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0) + self[name].__name = name + else: + try: + self[name] = toklist[0] + except (KeyError,TypeError,IndexError): + self[name] = toklist + + def __getitem__( self, i ): + if isinstance( i, (int,slice) ): + return self.__toklist[i] + else: + if i not in self.__accumNames: + return self.__tokdict[i][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[i] ]) + + def __setitem__( self, k, v, isinstance=isinstance ): + if isinstance(v,_ParseResultsWithOffset): + self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] + sub = v[0] + elif isinstance(k,int): + self.__toklist[k] = v + sub = v + else: + self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)] + sub = v + if isinstance(sub,ParseResults): + sub.__parent = wkref(self) + + def __delitem__( self, i ): + if isinstance(i,(int,slice)): + mylen = len( self.__toklist ) + del self.__toklist[i] + + # convert int to slice + if isinstance(i, int): + if i < 0: + i += mylen + i = slice(i, i+1) + # get removed indices + removed = list(range(*i.indices(mylen))) + removed.reverse() + # fixup indices in token dictionary + #~ for name in self.__tokdict: + #~ occurrences = self.__tokdict[name] + #~ for j in removed: + #~ for k, (value, position) in enumerate(occurrences): + #~ occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + for name,occurrences in self.__tokdict.items(): + for j in removed: + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) + else: + del self.__tokdict[i] + + def __contains__( self, k ): + return k in self.__tokdict + + def __len__( self ): return len( self.__toklist ) + def __bool__(self): return len( self.__toklist ) > 0 + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def iterkeys( self ): + """Returns all named result keys.""" + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def itervalues( self ): + """Returns all named result values.""" + return (self[k] for k in self.iterkeys()) + + def iteritems( self ): + return ((k, self[k]) for k in self.iterkeys()) + + if PY_3: + keys = iterkeys + values = itervalues + items = iteritems + else: + def keys( self ): + """Returns all named result keys.""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values.""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result keys and values as a list of tuples.""" + return list(self.iteritems()) + + def haskeys( self ): + """Since keys() returns an iterator, this method is helpful in bypassing + code that looks for the existence of any defined results names.""" + return bool(self.__tokdict) + + def pop( self, *args, **kwargs): + """Removes and returns item at specified index (default=last). + Supports both list and dict semantics for pop(). If passed no + argument or an integer argument, it will use list semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use dict + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in dict.pop().""" + if not args: + args = [-1] + for k,v in kwargs.items(): + if k == 'default': + args = (args[0], v) + else: + raise TypeError("pop() got an unexpected keyword argument '%s'" % k) + if (isinstance(args[0], int) or + len(args) == 1 or + args[0] in self): + index = args[0] + ret = self[index] + del self[index] + return ret + else: + defaultvalue = args[1] + return defaultvalue + + def get(self, key, defaultValue=None): + """Returns named result matching the given key, or if there is no + such name, then returns the given C{defaultValue} or C{None} if no + C{defaultValue} is specified.""" + if key in self: + return self[key] + else: + return defaultValue + + def insert( self, index, insStr ): + """Inserts new element at location index in the list of parsed tokens.""" + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + #~ for name in self.__tokdict: + #~ occurrences = self.__tokdict[name] + #~ for k, (value, position) in enumerate(occurrences): + #~ occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + for name,occurrences in self.__tokdict.items(): + for k, (value, position) in enumerate(occurrences): + occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) + + def append( self, item ): + """Add single element to end of ParseResults list of elements.""" + self.__toklist.append(item) + + def extend( self, itemseq ): + """Add sequence of elements to end of ParseResults list of elements.""" + if isinstance(itemseq, ParseResults): + self += itemseq + else: + self.__toklist.extend(itemseq) + + def clear( self ): + """Clear all elements and results names.""" + del self.__toklist[:] + self.__tokdict.clear() + + def __getattr__( self, name ): + try: + return self[name] + except KeyError: + return "" + + if name in self.__tokdict: + if name not in self.__accumNames: + return self.__tokdict[name][-1][0] + else: + return ParseResults([ v[0] for v in self.__tokdict[name] ]) + else: + return "" + + def __add__( self, other ): + ret = self.copy() + ret += other + return ret + + def __iadd__( self, other ): + if other.__tokdict: + offset = len(self.__toklist) + addoffset = lambda a: offset if a<0 else a+offset + otheritems = other.__tokdict.items() + otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) ) + for (k,vlist) in otheritems for v in vlist] + for k,v in otherdictitems: + self[k] = v + if isinstance(v[0],ParseResults): + v[0].__parent = wkref(self) + + self.__toklist += other.__toklist + self.__accumNames.update( other.__accumNames ) + return self + + def __radd__(self, other): + if isinstance(other,int) and other == 0: + return self.copy() + + def __repr__( self ): + return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) + + def __str__( self ): + return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']' + + def _asStringList( self, sep='' ): + out = [] + for item in self.__toklist: + if out and sep: + out.append(sep) + if isinstance( item, ParseResults ): + out += item._asStringList() + else: + out.append( _ustr(item) ) + return out + + def asList( self ): + """Returns the parse results as a nested list of matching tokens, all converted to strings.""" + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """Returns the named parse results as dictionary.""" + if PY_3: + return dict( self.items() ) + else: + return dict( self.iteritems() ) + + def copy( self ): + """Returns a new copy of a C{ParseResults} object.""" + ret = ParseResults( self.__toklist ) + ret.__tokdict = self.__tokdict.copy() + ret.__parent = self.__parent + ret.__accumNames.update( self.__accumNames ) + ret.__name = self.__name + return ret + + def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): + """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" + nl = "\n" + out = [] + namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items() + for v in vlist) + nextLevelIndent = indent + " " + + # collapse out indents if formatting is not desired + if not formatted: + indent = "" + nextLevelIndent = "" + nl = "" + + selfTag = None + if doctag is not None: + selfTag = doctag + else: + if self.__name: + selfTag = self.__name + + if not selfTag: + if namedItemsOnly: + return "" + else: + selfTag = "ITEM" + + out += [ nl, indent, "<", selfTag, ">" ] + + for i,res in enumerate(self.__toklist): + if isinstance(res,ParseResults): + if i in namedItems: + out += [ res.asXML(namedItems[i], + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + out += [ res.asXML(None, + namedItemsOnly and doctag is None, + nextLevelIndent, + formatted)] + else: + # individual token, see if there is a name for it + resTag = None + if i in namedItems: + resTag = namedItems[i] + if not resTag: + if namedItemsOnly: + continue + else: + resTag = "ITEM" + xmlBodyText = _xml_escape(_ustr(res)) + out += [ nl, nextLevelIndent, "<", resTag, ">", + xmlBodyText, + "</", resTag, ">" ] + + out += [ nl, indent, "</", selfTag, ">" ] + return "".join(out) + + def __lookup(self,sub): + for k,vlist in self.__tokdict.items(): + for v,loc in vlist: + if sub is v: + return k + return None + + def getName(self): + """Returns the results name for this token expression.""" + if self.__name: + return self.__name + elif self.__parent: + par = self.__parent() + if par: + return par.__lookup(self) + else: + return None + elif (len(self) == 1 and + len(self.__tokdict) == 1 and + self.__tokdict.values()[0][0][1] in (0,-1)): + return self.__tokdict.keys()[0] + else: + return None + + def dump(self,indent='',depth=0): + """Diagnostic method for listing out the contents of a C{ParseResults}. + Accepts an optional C{indent} argument so that this string can be embedded + in a nested display of other data.""" + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if self.haskeys(): + items = sorted(self.items()) + for k,v in items: + if out: + out.append(NL) + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v: + out.append( v.dump(indent,depth+1) ) + else: + out.append(_ustr(v)) + else: + out.append(_ustr(v)) + elif any(isinstance(vv,ParseResults) for vv in self): + v = self + for i,vv in enumerate(v): + if isinstance(vv,ParseResults): + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) + else: + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) + + return "".join(out) + + def pprint(self, *args, **kwargs): + """Pretty-printer for parsed results as a list, using the C{pprint} module. + Accepts additional positional or keyword args as defined for the + C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})""" + pprint.pprint(self.asList(), *args, **kwargs) + + # add support for pickle protocol + def __getstate__(self): + return ( self.__toklist, + ( self.__tokdict.copy(), + self.__parent is not None and self.__parent() or None, + self.__accumNames, + self.__name ) ) + + def __setstate__(self,state): + self.__toklist = state[0] + (self.__tokdict, + par, + inAccumNames, + self.__name) = state[1] + self.__accumNames = {} + self.__accumNames.update(inAccumNames) + if par is not None: + self.__parent = wkref(par) + else: + self.__parent = None + + def __dir__(self): + return dir(super(ParseResults,self)) + list(self.keys()) + +collections.MutableMapping.register(ParseResults) + +def col (loc,strg): + """Returns current column within a string, counting newlines as line separators. + The first column is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + s = strg + return 1 if loc<len(s) and s[loc] == '\n' else loc - s.rfind("\n", 0, loc) + +def lineno(loc,strg): + """Returns current line number within a string, counting newlines as line separators. + The first line is number 1. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{ParserElement.parseString}<ParserElement.parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + return strg.count("\n",0,loc) + 1 + +def line( loc, strg ): + """Returns the line of text containing loc within a string, counting newlines as line separators. + """ + lastCR = strg.rfind("\n", 0, loc) + nextCR = strg.find("\n", loc) + if nextCR >= 0: + return strg[lastCR+1:nextCR] + else: + return strg[lastCR+1:] + +def _defaultStartDebugAction( instring, loc, expr ): + print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))) + +def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ): + print ("Matched " + _ustr(expr) + " -> " + str(toks.asList())) + +def _defaultExceptionDebugAction( instring, loc, expr, exc ): + print ("Exception raised:" + _ustr(exc)) + +def nullDebugAction(*args): + """'Do-nothing' debug action, to suppress debugging output during parsing.""" + pass + +# Only works on Python 3.x - nonlocal is toxic to Python 2 installs +#~ 'decorator to trim function calls to match the arity of the target' +#~ def _trim_arity(func, maxargs=3): + #~ if func in singleArgBuiltins: + #~ return lambda s,l,t: func(t) + #~ limit = 0 + #~ foundArity = False + #~ def wrapper(*args): + #~ nonlocal limit,foundArity + #~ while 1: + #~ try: + #~ ret = func(*args[limit:]) + #~ foundArity = True + #~ return ret + #~ except TypeError: + #~ if limit == maxargs or foundArity: + #~ raise + #~ limit += 1 + #~ continue + #~ return wrapper + +# this version is Python 2.x-3.x cross-compatible +'decorator to trim function calls to match the arity of the target' +def _trim_arity(func, maxargs=2): + if func in singleArgBuiltins: + return lambda s,l,t: func(t) + limit = [0] + foundArity = [False] + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + if limit[0] <= maxargs and not foundArity[0]: + limit[0] += 1 + continue + raise + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + """Overrides the default whitespace chars + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + """ + ParserElement.literalStringClass = cls + + def __init__( self, savelist=False ): + self.parseAction = list() + self.failAction = None + #~ self.name = "<unknown>" # don't define self.name, let subclasses try/except upcall + self.strRepr = None + self.resultsName = None + self.saveAsList = savelist + self.skipWhitespace = True + self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + self.copyDefaultWhiteChars = True + self.mayReturnEmpty = False # used when checking for left-recursion + self.keepTabs = False + self.ignoreExprs = list() + self.debug = False + self.streamlined = False + self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index + self.errmsg = "" + self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all) + self.debugActions = ( None, None, None ) #custom debug actions + self.re = None + self.callPreparse = True # used to avoid redundant calls to preParse + self.callDuringTry = False + + def copy( self ): + """Make a copy of this C{ParserElement}. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element.""" + cpy = copy.copy( self ) + cpy.parseAction = self.parseAction[:] + cpy.ignoreExprs = self.ignoreExprs[:] + if self.copyDefaultWhiteChars: + cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS + return cpy + + def setName( self, name ): + """Define name for this expression, for use in debugging.""" + self.name = name + self.errmsg = "Expected " + self.name + if hasattr(self,"exception"): + self.exception.msg = self.errmsg + return self + + def setResultsName( self, name, listAllMatches=False ): + """Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original C{ParserElement} object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + + You can also set results names using the abbreviated syntax, + C{expr("name")} in place of C{expr.setResultsName("name")} - + see L{I{__call__}<__call__>}. + """ + newself = self.copy() + if name.endswith("*"): + name = name[:-1] + listAllMatches=True + newself.resultsName = name + newself.modalResults = not listAllMatches + return newself + + def setBreak(self,breakFlag = True): + """Method to invoke the Python pdb debugger when this element is + about to be parsed. Set C{breakFlag} to True to enable, False to + disable. + """ + if breakFlag: + _parseMethod = self._parse + def breaker(instring, loc, doActions=True, callPreParse=True): + import pdb + pdb.set_trace() + return _parseMethod( instring, loc, doActions, callPreParse ) + breaker._originalParseMethod = _parseMethod + self._parse = breaker + else: + if hasattr(self._parse,"_originalParseMethod"): + self._parse = self._parse._originalParseMethod + return self + + def setParseAction( self, *fns, **kwargs ): + """Define action to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, + C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}<parseString>} for more information + on parsing strings containing C{<TAB>}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """Add parse action to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}.""" + self.parseAction += list(map(_trim_arity, list(fns))) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def addCondition(self, *fns, **kwargs): + """Add a boolean predicate function to expression's list of parse actions. See + L{I{setParseAction}<setParseAction>}. Optional keyword argument C{message} can + be used to define a custom message to be used in the raised exception.""" + msg = kwargs.get("message") or "failed user-defined condition" + for fn in fns: + def pa(s,l,t): + if not bool(_trim_arity(fn)(s,l,t)): + raise ParseException(s,l,msg) + return t + self.parseAction.append(pa) + self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) + return self + + def setFailAction( self, fn ): + """Define action to perform if parsing fails at this expression. + Fail acton fn is a callable function that takes the arguments + C{fn(s,loc,expr,err)} where: + - s = string being parsed + - loc = location where expression match was attempted and failed + - expr = the parse expression that failed + - err = the exception thrown + The function returns no value. It may throw C{L{ParseFatalException}} + if it is desired to stop parsing immediately.""" + self.failAction = fn + return self + + def _skipIgnorables( self, instring, loc ): + exprsFound = True + while exprsFound: + exprsFound = False + for e in self.ignoreExprs: + try: + while 1: + loc,dummy = e._parse( instring, loc ) + exprsFound = True + except ParseException: + pass + return loc + + def preParse( self, instring, loc ): + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + + if self.skipWhitespace: + wt = self.whiteChars + instrlen = len(instring) + while loc < instrlen and instring[loc] in wt: + loc += 1 + + return loc + + def parseImpl( self, instring, loc, doActions=True ): + return loc, [] + + def postParse( self, instring, loc, tokenlist ): + return tokenlist + + #~ @profile + def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ): + debugging = ( self.debug ) #and doActions ) + + if debugging or self.failAction: + #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )) + if (self.debugActions[0] ): + self.debugActions[0]( instring, loc, self ) + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + try: + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + except ParseBaseException as err: + #~ print ("Exception raised:", err) + if self.debugActions[2]: + self.debugActions[2]( instring, tokensStart, self, err ) + if self.failAction: + self.failAction( instring, tokensStart, self, err ) + raise + else: + if callPreParse and self.callPreparse: + preloc = self.preParse( instring, loc ) + else: + preloc = loc + tokensStart = preloc + if self.mayIndexError or loc >= len(instring): + try: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + except IndexError: + raise ParseException( instring, len(instring), self.errmsg, self ) + else: + loc,tokens = self.parseImpl( instring, preloc, doActions ) + + tokens = self.postParse( instring, loc, tokens ) + + retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults ) + if self.parseAction and (doActions or self.callDuringTry): + if debugging: + try: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + except ParseBaseException as err: + #~ print "Exception raised in user parse action:", err + if (self.debugActions[2] ): + self.debugActions[2]( instring, tokensStart, self, err ) + raise + else: + for fn in self.parseAction: + tokens = fn( instring, tokensStart, retTokens ) + if tokens is not None: + retTokens = ParseResults( tokens, + self.resultsName, + asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), + modal=self.modalResults ) + + if debugging: + #~ print ("Matched",self,"->",retTokens.asList()) + if (self.debugActions[1] ): + self.debugActions[1]( instring, tokensStart, loc, self, retTokens ) + + return loc, retTokens + + def tryParse( self, instring, loc ): + try: + return self._parse( instring, loc, doActions=False )[0] + except ParseFatalException: + raise ParseException( instring, loc, self.errmsg, self) + + # this method gets repeatedly called during backtracking with the same arguments - + # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression + def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): + lookup = (self,instring,loc,callPreParse,doActions) + if lookup in ParserElement._exprArgCache: + value = ParserElement._exprArgCache[ lookup ] + if isinstance(value, Exception): + raise value + return (value[0],value[1].copy()) + else: + try: + value = self._parseNoCache( instring, loc, doActions, callPreParse ) + ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) + return value + except ParseBaseException as pe: + pe.__traceback__ = None + ParserElement._exprArgCache[ lookup ] = pe + raise + + _parse = _parseNoCache + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + _exprArgCache = {} + @staticmethod + def resetCache(): + ParserElement._exprArgCache.clear() + + _packratEnabled = False + @staticmethod + def enablePackrat(): + """Enables "packrat" parsing, which adds memoizing to the parsing logic. + Repeated parse attempts at the same string location (which happens + often in many complex grammars) can immediately return a cached value, + instead of re-executing parsing/validating code. Memoizing is done of + both valid results and parsing exceptions. + + This speedup may break existing programs that use parse actions that + have side-effects. For this reason, packrat parsing is disabled when + you first import pyparsing. To activate the packrat feature, your + program must call the class method C{ParserElement.enablePackrat()}. If + your program uses C{psyco} to "compile as you go", you must call + C{enablePackrat} before calling C{psyco.full()}. If you do not do this, + Python will crash. For best results, call C{enablePackrat()} immediately + after importing pyparsing. + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + ParserElement._parse = ParserElement._parseCache + + def parseString( self, instring, parseAll=False ): + """Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set C{parseAll} to True (equivalent to ending + the grammar with C{L{StringEnd()}}). + + Note: C{parseString} implicitly calls C{expandtabs()} on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the C{loc} argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling C{parseWithTabs} on your grammar before calling C{parseString} + (see L{I{parseWithTabs}<parseWithTabs>}) + - define your parse action using the full C{(s,loc,toks)} signature, and + reference the input string using the parse action's C{s} argument + - explictly expand the tabs in your input string before calling + C{parseString} + """ + ParserElement.resetCache() + if not self.streamlined: + self.streamline() + #~ self.saveAsList = True + for e in self.ignoreExprs: + e.streamline() + if not self.keepTabs: + instring = instring.expandtabs() + try: + loc, tokens = self._parse( instring, 0 ) + if parseAll: + loc = self.preParse( instring, loc ) + se = Empty() + StringEnd() + se._parse( instring, loc ) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + else: + return tokens + + def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): + """Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + C{maxMatches} argument, to clip scanning after 'n' matches are found. If + C{overlap} is specified, then overlapping matches will be reported. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}<parseString>} for more information on parsing + strings with embedded tabs.""" + if not self.streamlined: + self.streamline() + for e in self.ignoreExprs: + e.streamline() + + if not self.keepTabs: + instring = _ustr(instring).expandtabs() + instrlen = len(instring) + loc = 0 + preparseFn = self.preParse + parseFn = self._parse + ParserElement.resetCache() + matches = 0 + try: + while loc <= instrlen and matches < maxMatches: + try: + preloc = preparseFn( instring, loc ) + nextLoc,tokens = parseFn( instring, preloc, callPreParse=False ) + except ParseException: + loc = preloc+1 + else: + if nextLoc > loc: + matches += 1 + yield tokens, preloc, nextLoc + if overlap: + nextloc = preparseFn( instring, loc ) + if nextloc > loc: + loc = nextLoc + else: + loc += 1 + else: + loc = nextLoc + else: + loc = preloc+1 + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def transformString( self, instring ): + """Extension to C{L{scanString}}, to modify matching text with modified tokens that may + be returned from a parse action. To use C{transformString}, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking C{transformString()} on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. C{transformString()} returns the resulting transformed string.""" + out = [] + lastE = 0 + # force preservation of <TAB>s, to minimize unwanted transformation of string, and to + # keep string locs straight between transformString and scanString + self.keepTabs = True + try: + for t,s,e in self.scanString( instring ): + out.append( instring[lastE:s] ) + if t: + if isinstance(t,ParseResults): + out += t.asList() + elif isinstance(t,list): + out += t + else: + out.append(t) + lastE = e + out.append(instring[lastE:]) + out = [o for o in out if o] + return "".join(map(_ustr,_flatten(out))) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def searchString( self, instring, maxMatches=_MAX_INT ): + """Another extension to C{L{scanString}}, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + C{maxMatches} argument, to clip searching after 'n' matches are found. + """ + try: + return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def __add__(self, other ): + """Implementation of + operator - returns C{L{And}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, other ] ) + + def __radd__(self, other ): + """Implementation of + operator when left operand is not a C{L{ParserElement}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other + self + + def __sub__(self, other): + """Implementation of - operator, returns C{L{And}} with error stop""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return And( [ self, And._ErrorStop(), other ] ) + + def __rsub__(self, other ): + """Implementation of - operator when left operand is not a C{L{ParserElement}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other - self + + def __mul__(self,other): + """Implementation of * operator, allows use of C{expr * 3} in place of + C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer + tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples + may also include C{None} as in: + - C{expr*(n,None)} or C{expr*(n,)} is equivalent + to C{expr*n + L{ZeroOrMore}(expr)} + (read as "at least n instances of C{expr}") + - C{expr*(None,n)} is equivalent to C{expr*(0,n)} + (read as "0 to n instances of C{expr}") + - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} + - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} + + Note that C{expr*(None,n)} does not raise an exception if + more than n exprs exist in the input stream; that is, + C{expr*(None,n)} does not enforce a maximum number of expr + occurrences. If this behavior is desired, then write + C{expr*(None,n) + ~expr} + + """ + if isinstance(other,int): + minElements, optElements = other,0 + elif isinstance(other,tuple): + other = (other + (None, None))[:2] + if other[0] is None: + other = (0, other[1]) + if isinstance(other[0],int) and other[1] is None: + if other[0] == 0: + return ZeroOrMore(self) + if other[0] == 1: + return OneOrMore(self) + else: + return self*other[0] + ZeroOrMore(self) + elif isinstance(other[0],int) and isinstance(other[1],int): + minElements, optElements = other + optElements -= minElements + else: + raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1])) + else: + raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other)) + + if minElements < 0: + raise ValueError("cannot multiply ParserElement by negative value") + if optElements < 0: + raise ValueError("second tuple value must be greater or equal to first tuple value") + if minElements == optElements == 0: + raise ValueError("cannot multiply ParserElement by 0 or (0,0)") + + if (optElements): + def makeOptionalList(n): + if n>1: + return Optional(self + makeOptionalList(n-1)) + else: + return Optional(self) + if minElements: + if minElements == 1: + ret = self + makeOptionalList(optElements) + else: + ret = And([self]*minElements) + makeOptionalList(optElements) + else: + ret = makeOptionalList(optElements) + else: + if minElements == 1: + ret = self + else: + ret = And([self]*minElements) + return ret + + def __rmul__(self, other): + return self.__mul__(other) + + def __or__(self, other ): + """Implementation of | operator - returns C{L{MatchFirst}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return MatchFirst( [ self, other ] ) + + def __ror__(self, other ): + """Implementation of | operator when left operand is not a C{L{ParserElement}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other | self + + def __xor__(self, other ): + """Implementation of ^ operator - returns C{L{Or}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Or( [ self, other ] ) + + def __rxor__(self, other ): + """Implementation of ^ operator when left operand is not a C{L{ParserElement}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other ^ self + + def __and__(self, other ): + """Implementation of & operator - returns C{L{Each}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return Each( [ self, other ] ) + + def __rand__(self, other ): + """Implementation of & operator when left operand is not a C{L{ParserElement}}""" + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + if not isinstance( other, ParserElement ): + warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), + SyntaxWarning, stacklevel=2) + return None + return other & self + + def __invert__( self ): + """Implementation of ~ operator - returns C{L{NotAny}}""" + return NotAny( self ) + + def __call__(self, name=None): + """Shortcut for C{L{setResultsName}}, with C{listAllMatches=default}:: + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + could be written as:: + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + + If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be + passed as C{True}. + + If C{name} is omitted, same as calling C{L{copy}}. + """ + if name is not None: + return self.setResultsName(name) + else: + return self.copy() + + def suppress( self ): + """Suppresses the output of this C{ParserElement}; useful to keep punctuation from + cluttering up returned output. + """ + return Suppress( self ) + + def leaveWhitespace( self ): + """Disables the skipping of whitespace before matching the characters in the + C{ParserElement}'s defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + self.skipWhitespace = False + return self + + def setWhitespaceChars( self, chars ): + """Overrides the default whitespace chars + """ + self.skipWhitespace = True + self.whiteChars = chars + self.copyDefaultWhiteChars = False + return self + + def parseWithTabs( self ): + """Overrides default behavior to expand C{<TAB>}s to spaces before parsing the input string. + Must be called before C{parseString} when the input grammar contains elements that + match C{<TAB>} characters.""" + self.keepTabs = True + return self + + def ignore( self, other ): + """Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + """ + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append( other.copy() ) + else: + self.ignoreExprs.append( Suppress( other.copy() ) ) + return self + + def setDebugActions( self, startAction, successAction, exceptionAction ): + """Enable display of debugging messages while doing pattern matching.""" + self.debugActions = (startAction or _defaultStartDebugAction, + successAction or _defaultSuccessDebugAction, + exceptionAction or _defaultExceptionDebugAction) + self.debug = True + return self + + def setDebug( self, flag=True ): + """Enable display of debugging messages while doing pattern matching. + Set C{flag} to True to enable, False to disable.""" + if flag: + self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) + else: + self.debug = False + return self + + def __str__( self ): + return self.name + + def __repr__( self ): + return _ustr(self) + + def streamline( self ): + self.streamlined = True + self.strRepr = None + return self + + def checkRecursion( self, parseElementList ): + pass + + def validate( self, validateTrace=[] ): + """Check defined expressions for valid structure, check for infinite recursive definitions.""" + self.checkRecursion( [] ) + + def parseFile( self, file_or_filename, parseAll=False ): + """Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. + """ + try: + file_contents = file_or_filename.read() + except AttributeError: + f = open(file_or_filename, "r") + file_contents = f.read() + f.close() + try: + return self.parseString(file_contents, parseAll) + except ParseBaseException as exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc + + def __eq__(self,other): + if isinstance(other, ParserElement): + return self is other or self.__dict__ == other.__dict__ + elif isinstance(other, basestring): + try: + self.parseString(_ustr(other), parseAll=True) + return True + except ParseBaseException: + return False + else: + return super(ParserElement,self)==other + + def __ne__(self,other): + return not (self == other) + + def __hash__(self): + return hash(id(self)) + + def __req__(self,other): + return self == other + + def __rne__(self,other): + return not (self == other) + + def runTests(self, tests, parseAll=False): + """Execute the parse expression on a series of test strings, showing each + test, the parsed results or where the parse failed. Quick and easy way to + run a parse expression against a list of sample strings. + + Parameters: + - tests - a list of separate test strings, or a multiline string of test strings + - parseAll - (default=False) - flag to pass to C{L{parseString}} when running tests + """ + if isinstance(tests, basestring): + tests = map(str.strip, tests.splitlines()) + for t in tests: + out = [t] + try: + out.append(self.parseString(t, parseAll=parseAll).dump()) + except ParseException as pe: + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^') + else: + out.append(' '*pe.loc + '^') + out.append(str(pe)) + out.append('') + print('\n'.join(out)) + + +class Token(ParserElement): + """Abstract C{ParserElement} subclass, for defining atomic matching patterns.""" + def __init__( self ): + super(Token,self).__init__( savelist=False ) + + +class Empty(Token): + """An empty token, will always match.""" + def __init__( self ): + super(Empty,self).__init__() + self.name = "Empty" + self.mayReturnEmpty = True + self.mayIndexError = False + + +class NoMatch(Token): + """A token that will never match.""" + def __init__( self ): + super(NoMatch,self).__init__() + self.name = "NoMatch" + self.mayReturnEmpty = True + self.mayIndexError = False + self.errmsg = "Unmatchable token" + + def parseImpl( self, instring, loc, doActions=True ): + raise ParseException(instring, loc, self.errmsg, self) + + +class Literal(Token): + """Token to exactly match a specified string.""" + def __init__( self, matchString ): + super(Literal,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Literal; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.__class__ = Empty + self.name = '"%s"' % _ustr(self.match) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + + # Performance tuning: this routine gets called a *lot* + # if this is a single character match string and the first character matches, + # short-circuit as quickly as possible, and avoid calling startswith + #~ @profile + def parseImpl( self, instring, loc, doActions=True ): + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) +_L = Literal +ParserElement.literalStringClass = Literal + +class Keyword(Token): + """Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with C{L{Literal}}:: + Literal("if") will match the leading C{'if'} in C{'ifAndOnlyIf'}. + Keyword("if") will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} + Accepts two optional constructor arguments in addition to the keyword string: + C{identChars} is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$"; C{caseless} allows case-insensitive + matching, default is C{False}. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): + super(Keyword,self).__init__() + self.match = matchString + self.matchLen = len(matchString) + try: + self.firstMatchChar = matchString[0] + except IndexError: + warnings.warn("null string passed to Keyword; use Empty() instead", + SyntaxWarning, stacklevel=2) + self.name = '"%s"' % self.match + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = False + self.mayIndexError = False + self.caseless = caseless + if caseless: + self.caselessmatch = matchString.upper() + identChars = identChars.upper() + self.identChars = set(identChars) + + def parseImpl( self, instring, loc, doActions=True ): + if self.caseless: + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and + (loc == 0 or instring[loc-1].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + else: + if (instring[loc] == self.firstMatchChar and + (self.matchLen==1 or instring.startswith(self.match,loc)) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and + (loc == 0 or instring[loc-1] not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + + def copy(self): + c = super(Keyword,self).copy() + c.identChars = Keyword.DEFAULT_KEYWORD_CHARS + return c + + @staticmethod + def setDefaultKeywordChars( chars ): + """Overrides the default Keyword chars + """ + Keyword.DEFAULT_KEYWORD_CHARS = chars + +class CaselessLiteral(Literal): + """Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + """ + def __init__( self, matchString ): + super(CaselessLiteral,self).__init__( matchString.upper() ) + # Preserve the defining literal. + self.returnString = matchString + self.name = "'%s'" % self.returnString + self.errmsg = "Expected " + self.name + + def parseImpl( self, instring, loc, doActions=True ): + if instring[ loc:loc+self.matchLen ].upper() == self.match: + return loc+self.matchLen, self.returnString + raise ParseException(instring, loc, self.errmsg, self) + +class CaselessKeyword(Keyword): + def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + + def parseImpl( self, instring, loc, doActions=True ): + if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and + (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ): + return loc+self.matchLen, self.match + raise ParseException(instring, loc, self.errmsg, self) + +class Word(Token): + """Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. An optional + C{exclude} parameter can list characters that might be found in + the input C{bodyChars} string; useful to define a word of all printables + except for one or two characters, for instance. + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): + super(Word,self).__init__() + if excludeChars: + initChars = ''.join(c for c in initChars if c not in excludeChars) + if bodyChars: + bodyChars = ''.join(c for c in bodyChars if c not in excludeChars) + self.initCharsOrig = initChars + self.initChars = set(initChars) + if bodyChars : + self.bodyCharsOrig = bodyChars + self.bodyChars = set(bodyChars) + else: + self.bodyCharsOrig = initChars + self.bodyChars = set(initChars) + + self.maxSpecified = max > 0 + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.asKeyword = asKeyword + + if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0): + if self.bodyCharsOrig == self.initCharsOrig: + self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig) + elif len(self.initCharsOrig) == 1: + self.reString = "%s[%s]*" % \ + (re.escape(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + else: + self.reString = "[%s][%s]*" % \ + (_escapeRegexRangeChars(self.initCharsOrig), + _escapeRegexRangeChars(self.bodyCharsOrig),) + if self.asKeyword: + self.reString = r"\b"+self.reString+r"\b" + try: + self.re = re.compile( self.reString ) + except: + self.re = None + + def parseImpl( self, instring, loc, doActions=True ): + if self.re: + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + return loc, result.group() + + if not(instring[ loc ] in self.initChars): + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + instrlen = len(instring) + bodychars = self.bodyChars + maxloc = start + self.maxLen + maxloc = min( maxloc, instrlen ) + while loc < maxloc and instring[loc] in bodychars: + loc += 1 + + throwException = False + if loc - start < self.minLen: + throwException = True + if self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + if self.asKeyword: + if (start>0 and instring[start-1] in bodychars) or (loc<instrlen and instring[loc] in bodychars): + throwException = True + + if throwException: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(Word,self).__str__() + except: + pass + + + if self.strRepr is None: + + def charsAsStr(s): + if len(s)>4: + return s[:4]+"..." + else: + return s + + if ( self.initCharsOrig != self.bodyCharsOrig ): + self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) ) + else: + self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig) + + return self.strRepr + + +class Regex(Token): + """Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + """ + compiledREtype = type(re.compile("[A-Z]")) + def __init__( self, pattern, flags=0): + """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags.""" + super(Regex,self).__init__() + + if isinstance(pattern, basestring): + if len(pattern) == 0: + warnings.warn("null string passed to Regex; use Empty() instead", + SyntaxWarning, stacklevel=2) + + self.pattern = pattern + self.flags = flags + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % pattern, + SyntaxWarning, stacklevel=2) + raise + + elif isinstance(pattern, Regex.compiledREtype): + self.re = pattern + self.pattern = \ + self.reString = str(pattern) + self.flags = flags + + else: + raise ValueError("Regex may only be constructed with a string or a compiled RE object") + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + d = result.groupdict() + ret = ParseResults(result.group()) + if d: + for k in d: + ret[k] = d[k] + return loc,ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + """Token for matching strings that are delimited by quoting characters. + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): + """ + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=None) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) + - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) + """ + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if len(quoteChar) == 0: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if len(endQuoteChar) == 0: + warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + self.quoteChar = quoteChar + self.quoteCharLen = len(quoteChar) + self.firstQuoteChar = quoteChar[0] + self.endQuoteChar = endQuoteChar + self.endQuoteCharLen = len(endQuoteChar) + self.escChar = escChar + self.escQuote = escQuote + self.unquoteResults = unquoteResults + + if multiline: + self.flags = re.MULTILINE | re.DOTALL + self.pattern = r'%s(?:[^%s%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + else: + self.flags = 0 + self.pattern = r'%s(?:[^%s\n\r%s]' % \ + ( re.escape(self.quoteChar), + _escapeRegexRangeChars(self.endQuoteChar[0]), + (escChar is not None and _escapeRegexRangeChars(escChar) or '') ) + if len(self.endQuoteChar) > 1: + self.pattern += ( + '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]), + _escapeRegexRangeChars(self.endQuoteChar[i])) + for i in range(len(self.endQuoteChar)-1,0,-1)) + ')' + ) + if escQuote: + self.pattern += (r'|(?:%s)' % re.escape(escQuote)) + if escChar: + self.pattern += (r'|(?:%s.)' % re.escape(escChar)) + self.escCharReplacePattern = re.escape(self.escChar)+"(.)" + self.pattern += (r')*%s' % re.escape(self.endQuoteChar)) + + try: + self.re = re.compile(self.pattern, self.flags) + self.reString = self.pattern + except sre_constants.error: + warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern, + SyntaxWarning, stacklevel=2) + raise + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayIndexError = False + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + ret = result.group() + + if self.unquoteResults: + + # strip off quotes + ret = ret[self.quoteCharLen:-self.endQuoteCharLen] + + if isinstance(ret,basestring): + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) + + # replace escaped quotes + if self.escQuote: + ret = ret.replace(self.escQuote, self.endQuoteChar) + + return loc, ret + + def __str__( self ): + try: + return super(QuotedString,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar) + + return self.strRepr + + +class CharsNotIn(Token): + """Token for matching words composed of characters *not* in a given set. + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. + """ + def __init__( self, notChars, min=1, max=0, exact=0 ): + super(CharsNotIn,self).__init__() + self.skipWhitespace = False + self.notChars = notChars + + if min < 1: + raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted") + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + self.name = _ustr(self) + self.errmsg = "Expected " + self.name + self.mayReturnEmpty = ( self.minLen == 0 ) + self.mayIndexError = False + + def parseImpl( self, instring, loc, doActions=True ): + if instring[loc] in self.notChars: + raise ParseException(instring, loc, self.errmsg, self) + + start = loc + loc += 1 + notchars = self.notChars + maxlen = min( start+self.maxLen, len(instring) ) + while loc < maxlen and \ + (instring[loc] not in notchars): + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + def __str__( self ): + try: + return super(CharsNotIn, self).__str__() + except: + pass + + if self.strRepr is None: + if len(self.notChars) > 4: + self.strRepr = "!W:(%s...)" % self.notChars[:4] + else: + self.strRepr = "!W:(%s)" % self.notChars + + return self.strRepr + +class White(Token): + """Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, + as defined for the C{L{Word}} class.""" + whiteStrs = { + " " : "<SPC>", + "\t": "<TAB>", + "\n": "<LF>", + "\r": "<CR>", + "\f": "<FF>", + } + def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0): + super(White,self).__init__() + self.matchWhite = ws + self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) ) + #~ self.leaveWhitespace() + self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite)) + self.mayReturnEmpty = True + self.errmsg = "Expected " + self.name + + self.minLen = min + + if max > 0: + self.maxLen = max + else: + self.maxLen = _MAX_INT + + if exact > 0: + self.maxLen = exact + self.minLen = exact + + def parseImpl( self, instring, loc, doActions=True ): + if not(instring[ loc ] in self.matchWhite): + raise ParseException(instring, loc, self.errmsg, self) + start = loc + loc += 1 + maxloc = start + self.maxLen + maxloc = min( maxloc, len(instring) ) + while loc < maxloc and instring[loc] in self.matchWhite: + loc += 1 + + if loc - start < self.minLen: + raise ParseException(instring, loc, self.errmsg, self) + + return loc, instring[start:loc] + + +class _PositionToken(Token): + def __init__( self ): + super(_PositionToken,self).__init__() + self.name=self.__class__.__name__ + self.mayReturnEmpty = True + self.mayIndexError = False + +class GoToColumn(_PositionToken): + """Token to advance to a specific column of input text; useful for tabular report scraping.""" + def __init__( self, colno ): + super(GoToColumn,self).__init__() + self.col = colno + + def preParse( self, instring, loc ): + if col(loc,instring) != self.col: + instrlen = len(instring) + if self.ignoreExprs: + loc = self._skipIgnorables( instring, loc ) + while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col : + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + thiscol = col( loc, instring ) + if thiscol > self.col: + raise ParseException( instring, loc, "Text not in expected column", self ) + newloc = loc + self.col - thiscol + ret = instring[ loc: newloc ] + return newloc, ret + +class LineStart(_PositionToken): + """Matches if current position is at the beginning of a line within the parse string""" + def __init__( self ): + super(LineStart,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected start of line" + + def preParse( self, instring, loc ): + preloc = super(LineStart,self).preParse(instring,loc) + if instring[preloc] == "\n": + loc += 1 + return loc + + def parseImpl( self, instring, loc, doActions=True ): + if not( loc==0 or + (loc == self.preParse( instring, 0 )) or + (instring[loc-1] == "\n") ): #col(loc, instring) != 1: + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class LineEnd(_PositionToken): + """Matches if current position is at the end of a line within the parse string""" + def __init__( self ): + super(LineEnd,self).__init__() + self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) + self.errmsg = "Expected end of line" + + def parseImpl( self, instring, loc, doActions=True ): + if loc<len(instring): + if instring[loc] == "\n": + return loc+1, "\n" + else: + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class StringStart(_PositionToken): + """Matches if current position is at the beginning of the parse string""" + def __init__( self ): + super(StringStart,self).__init__() + self.errmsg = "Expected start of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc != 0: + # see if entire string up to here is just whitespace and ignoreables + if loc != self.preParse( instring, 0 ): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class StringEnd(_PositionToken): + """Matches if current position is at the end of the parse string""" + def __init__( self ): + super(StringEnd,self).__init__() + self.errmsg = "Expected end of text" + + def parseImpl( self, instring, loc, doActions=True ): + if loc < len(instring): + raise ParseException(instring, loc, self.errmsg, self) + elif loc == len(instring): + return loc+1, [] + elif loc > len(instring): + return loc, [] + else: + raise ParseException(instring, loc, self.errmsg, self) + +class WordStart(_PositionToken): + """Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of + the string being parsed, or at the beginning of a line. + """ + def __init__(self, wordChars = printables): + super(WordStart,self).__init__() + self.wordChars = set(wordChars) + self.errmsg = "Not at the start of a word" + + def parseImpl(self, instring, loc, doActions=True ): + if loc != 0: + if (instring[loc-1] in self.wordChars or + instring[loc] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + +class WordEnd(_PositionToken): + """Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of + the string being parsed, or at the end of a line. + """ + def __init__(self, wordChars = printables): + super(WordEnd,self).__init__() + self.wordChars = set(wordChars) + self.skipWhitespace = False + self.errmsg = "Not at the end of a word" + + def parseImpl(self, instring, loc, doActions=True ): + instrlen = len(instring) + if instrlen>0 and loc<instrlen: + if (instring[loc] in self.wordChars or + instring[loc-1] not in self.wordChars): + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + +class ParseExpression(ParserElement): + """Abstract subclass of ParserElement, for combining and post-processing parsed tokens.""" + def __init__( self, exprs, savelist = False ): + super(ParseExpression,self).__init__(savelist) + if isinstance( exprs, _generatorType ): + exprs = list(exprs) + + if isinstance( exprs, basestring ): + self.exprs = [ Literal( exprs ) ] + elif isinstance( exprs, collections.Sequence ): + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(Literal, exprs) + self.exprs = list(exprs) + else: + try: + self.exprs = list( exprs ) + except TypeError: + self.exprs = [ exprs ] + self.callPreparse = False + + def __getitem__( self, i ): + return self.exprs[i] + + def append( self, other ): + self.exprs.append( other ) + self.strRepr = None + return self + + def leaveWhitespace( self ): + """Extends C{leaveWhitespace} defined in base class, and also invokes C{leaveWhitespace} on + all contained expressions.""" + self.skipWhitespace = False + self.exprs = [ e.copy() for e in self.exprs ] + for e in self.exprs: + e.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + else: + super( ParseExpression, self).ignore( other ) + for e in self.exprs: + e.ignore( self.ignoreExprs[-1] ) + return self + + def __str__( self ): + try: + return super(ParseExpression,self).__str__() + except: + pass + + if self.strRepr is None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.exprs) ) + return self.strRepr + + def streamline( self ): + super(ParseExpression,self).streamline() + + for e in self.exprs: + e.streamline() + + # collapse nested And's of the form And( And( And( a,b), c), d) to And( a,b,c,d ) + # but only if there are no parse actions or resultsNames on the nested And's + # (likewise for Or's and MatchFirst's) + if ( len(self.exprs) == 2 ): + other = self.exprs[0] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = other.exprs[:] + [ self.exprs[1] ] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + other = self.exprs[-1] + if ( isinstance( other, self.__class__ ) and + not(other.parseAction) and + other.resultsName is None and + not other.debug ): + self.exprs = self.exprs[:-1] + other.exprs[:] + self.strRepr = None + self.mayReturnEmpty |= other.mayReturnEmpty + self.mayIndexError |= other.mayIndexError + + self.errmsg = "Expected " + str(self) + + return self + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ParseExpression,self).setResultsName(name,listAllMatches) + return ret + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + for e in self.exprs: + e.validate(tmp) + self.checkRecursion( [] ) + + def copy(self): + ret = super(ParseExpression,self).copy() + ret.exprs = [e.copy() for e in self.exprs] + return ret + +class And(ParseExpression): + """Requires all given C{ParseExpression}s to be found in the given order. + Expressions may be separated by whitespace. + May be constructed using the C{'+'} operator. + """ + + class _ErrorStop(Empty): + def __init__(self, *args, **kwargs): + super(And._ErrorStop,self).__init__(*args, **kwargs) + self.name = '-' + self.leaveWhitespace() + + def __init__( self, exprs, savelist = True ): + super(And,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.setWhitespaceChars( self.exprs[0].whiteChars ) + self.skipWhitespace = self.exprs[0].skipWhitespace + self.callPreparse = True + + def parseImpl( self, instring, loc, doActions=True ): + # pass False as last arg to _parse for first element, since we already + # pre-parsed the string as part of our And pre-parsing + loc, resultlist = self.exprs[0]._parse( instring, loc, doActions, callPreParse=False ) + errorStop = False + for e in self.exprs[1:]: + if isinstance(e, And._ErrorStop): + errorStop = True + continue + if errorStop: + try: + loc, exprtokens = e._parse( instring, loc, doActions ) + except ParseSyntaxException: + raise + except ParseBaseException as pe: + pe.__traceback__ = None + raise ParseSyntaxException(pe) + except IndexError: + raise ParseSyntaxException( ParseException(instring, len(instring), self.errmsg, self) ) + else: + loc, exprtokens = e._parse( instring, loc, doActions ) + if exprtokens or exprtokens.haskeys(): + resultlist += exprtokens + return loc, resultlist + + def __iadd__(self, other ): + if isinstance( other, basestring ): + other = Literal( other ) + return self.append( other ) #And( [ self, other ] ) + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + if not e.mayReturnEmpty: + break + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + +class Or(ParseExpression): + """Requires that at least one C{ParseExpression} is found. + If two expressions match, the expression that matches the longest string will be used. + May be constructed using the C{'^'} operator. + """ + def __init__( self, exprs, savelist = False ): + super(Or,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + matches = [] + for e in self.exprs: + try: + loc2 = e.tryParse( instring, loc ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + else: + # save match among all matches, to retry longest to shortest + matches.append((loc2, e)) + + if matches: + matches.sort(key=lambda x: -x[0]) + for _,e in matches: + try: + return e._parse( instring, loc, doActions ) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + + def __ixor__(self, other ): + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + return self.append( other ) #Or( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class MatchFirst(ParseExpression): + """Requires that at least one C{ParseExpression} is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the C{'|'} operator. + """ + def __init__( self, exprs, savelist = False ): + super(MatchFirst,self).__init__(exprs, savelist) + if self.exprs: + self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) + else: + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + maxExcLoc = -1 + maxException = None + for e in self.exprs: + try: + ret = e._parse( instring, loc, doActions ) + return ret + except ParseException as err: + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + except IndexError: + if len(instring) > maxExcLoc: + maxException = ParseException(instring,len(instring),e.errmsg,self) + maxExcLoc = len(instring) + + # only got here if no expression matched, raise exception for match that made it the furthest + else: + if maxException is not None: + maxException.msg = self.errmsg + raise maxException + else: + raise ParseException(instring, loc, "no defined alternatives to match", self) + + def __ior__(self, other ): + if isinstance( other, basestring ): + other = ParserElement.literalStringClass( other ) + return self.append( other ) #MatchFirst( [ self, other ] ) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class Each(ParseExpression): + """Requires all given C{ParseExpression}s to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the C{'&'} operator. + """ + def __init__( self, exprs, savelist = True ): + super(Each,self).__init__(exprs, savelist) + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + self.skipWhitespace = True + self.initExprGroups = True + + def parseImpl( self, instring, loc, doActions=True ): + if self.initExprGroups: + self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional)) + opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ] + opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)] + self.optionals = opt1 + opt2 + self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ] + self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ] + self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ] + self.required += self.multirequired + self.initExprGroups = False + tmpLoc = loc + tmpReqd = self.required[:] + tmpOpt = self.optionals[:] + matchOrder = [] + + keepMatching = True + while keepMatching: + tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired + failed = [] + for e in tmpExprs: + try: + tmpLoc = e.tryParse( instring, tmpLoc ) + except ParseException: + failed.append(e) + else: + matchOrder.append(self.opt1map.get(id(e),e)) + if e in tmpReqd: + tmpReqd.remove(e) + elif e in tmpOpt: + tmpOpt.remove(e) + if len(failed) == len(tmpExprs): + keepMatching = False + + if tmpReqd: + missing = ", ".join(_ustr(e) for e in tmpReqd) + raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing ) + + # add any unmatched Optionals, in case they have default values defined + matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt] + + resultlist = [] + for e in matchOrder: + loc,results = e._parse(instring,loc,doActions) + resultlist.append(results) + + finalResults = ParseResults([]) + for r in resultlist: + dups = {} + for k in r.keys(): + if k in finalResults: + tmp = ParseResults(finalResults[k]) + tmp += ParseResults(r[k]) + dups[k] = tmp + finalResults += ParseResults(r) + for k,v in dups.items(): + finalResults[k] = v + return loc, finalResults + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}" + + return self.strRepr + + def checkRecursion( self, parseElementList ): + subRecCheckList = parseElementList[:] + [ self ] + for e in self.exprs: + e.checkRecursion( subRecCheckList ) + + +class ParseElementEnhance(ParserElement): + """Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.""" + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + expr = Literal(expr) + self.expr = expr + self.strRepr = None + if expr is not None: + self.mayIndexError = expr.mayIndexError + self.mayReturnEmpty = expr.mayReturnEmpty + self.setWhitespaceChars( expr.whiteChars ) + self.skipWhitespace = expr.skipWhitespace + self.saveAsList = expr.saveAsList + self.callPreparse = expr.callPreparse + self.ignoreExprs.extend(expr.ignoreExprs) + + def parseImpl( self, instring, loc, doActions=True ): + if self.expr is not None: + return self.expr._parse( instring, loc, doActions, callPreParse=False ) + else: + raise ParseException("",loc,self.errmsg,self) + + def leaveWhitespace( self ): + self.skipWhitespace = False + self.expr = self.expr.copy() + if self.expr is not None: + self.expr.leaveWhitespace() + return self + + def ignore( self, other ): + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + else: + super( ParseElementEnhance, self).ignore( other ) + if self.expr is not None: + self.expr.ignore( self.ignoreExprs[-1] ) + return self + + def streamline( self ): + super(ParseElementEnhance,self).streamline() + if self.expr is not None: + self.expr.streamline() + return self + + def checkRecursion( self, parseElementList ): + if self in parseElementList: + raise RecursiveGrammarException( parseElementList+[self] ) + subRecCheckList = parseElementList[:] + [ self ] + if self.expr is not None: + self.expr.checkRecursion( subRecCheckList ) + + def validate( self, validateTrace=[] ): + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion( [] ) + + def __str__( self ): + try: + return super(ParseElementEnhance,self).__str__() + except: + pass + + if self.strRepr is None and self.expr is not None: + self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) ) + return self.strRepr + + +class FollowedBy(ParseElementEnhance): + """Lookahead matching of the given parse expression. C{FollowedBy} + does *not* advance the parsing position within the input string, it only + verifies that the specified parse expression matches at the current + position. C{FollowedBy} always returns a null token list.""" + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + self.expr.tryParse( instring, loc ) + return loc, [] + + +class NotAny(ParseElementEnhance): + """Lookahead to disallow matching with the given parse expression. C{NotAny} + does *not* advance the parsing position within the input string, it only + verifies that the specified parse expression does *not* match at the current + position. Also, C{NotAny} does *not* skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator.""" + def __init__( self, expr ): + super(NotAny,self).__init__(expr) + #~ self.leaveWhitespace() + self.skipWhitespace = False # do NOT use self.leaveWhitespace(), don't want to propagate to exprs + self.mayReturnEmpty = True + self.errmsg = "Found unwanted token, "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + try: + self.expr.tryParse( instring, loc ) + except (ParseException,IndexError): + pass + else: + raise ParseException(instring, loc, self.errmsg, self) + return loc, [] + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "~{" + _ustr(self.expr) + "}" + + return self.strRepr + + +class ZeroOrMore(ParseElementEnhance): + """Optional repetition of zero or more of the given expression.""" + def __init__( self, expr ): + super(ZeroOrMore,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + tokens = [] + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + while 1: + if hasIgnoreExprs: + preloc = self._skipIgnorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + if tmptokens or tmptokens.haskeys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]..." + + return self.strRepr + + def setResultsName( self, name, listAllMatches=False ): + ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) + ret.saveAsList = True + return ret + + +class OneOrMore(ParseElementEnhance): + """Repetition of one or more of the given expression.""" + def parseImpl( self, instring, loc, doActions=True ): + # must be at least one + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + while 1: + if hasIgnoreExprs: + preloc = self._skipIgnorables( instring, loc ) + else: + preloc = loc + loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + if tmptokens or tmptokens.haskeys(): + tokens += tmptokens + except (ParseException,IndexError): + pass + + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "{" + _ustr(self.expr) + "}..." + + return self.strRepr + + def setResultsName( self, name, listAllMatches=False ): + ret = super(OneOrMore,self).setResultsName(name,listAllMatches) + ret.saveAsList = True + return ret + +class _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """Optional matching of the given expression. + A default return string can also be specified, if the optional expression + is not found. + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.defaultValue = default + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) + except (ParseException,IndexError): + if self.defaultValue is not _optionalNotMatched: + if self.expr.resultsName: + tokens = ParseResults([ self.defaultValue ]) + tokens[self.expr.resultsName] = self.defaultValue + else: + tokens = [ self.defaultValue ] + else: + tokens = [] + return loc, tokens + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + if self.strRepr is None: + self.strRepr = "[" + _ustr(self.expr) + "]" + + return self.strRepr + + +class SkipTo(ParseElementEnhance): + """Token for skipping over all undefined text until the matched expression is found. + If C{include} is set to true, the matched expression is also parsed (the skipped text + and matched expression are returned as a 2-element list). The C{ignore} + argument is used to define grammars (typically quoted strings and comments) that + might contain false matches. + """ + def __init__( self, other, include=False, ignore=None, failOn=None ): + super( SkipTo, self ).__init__( other ) + self.ignoreExpr = ignore + self.mayReturnEmpty = True + self.mayIndexError = False + self.includeMatch = include + self.asList = False + if failOn is not None and isinstance(failOn, basestring): + self.failOn = Literal(failOn) + else: + self.failOn = failOn + self.errmsg = "No match found for "+_ustr(self.expr) + + def parseImpl( self, instring, loc, doActions=True ): + startLoc = loc + instrlen = len(instring) + expr = self.expr + failParse = False + while loc <= instrlen: + try: + if self.failOn: + try: + self.failOn.tryParse(instring, loc) + except ParseBaseException: + pass + else: + failParse = True + raise ParseException(instring, loc, "Found expression " + str(self.failOn)) + failParse = False + if self.ignoreExpr is not None: + while 1: + try: + loc = self.ignoreExpr.tryParse(instring,loc) + # print("found ignoreExpr, advance to", loc) + except ParseBaseException: + break + expr._parse( instring, loc, doActions=False, callPreParse=False ) + skipText = instring[startLoc:loc] + if self.includeMatch: + loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) + if mat: + skipRes = ParseResults( skipText ) + skipRes += mat + return loc, [ skipRes ] + else: + return loc, [ skipText ] + else: + return loc, [ skipText ] + except (ParseException,IndexError): + if failParse: + raise + else: + loc += 1 + raise ParseException(instring, loc, self.errmsg, self) + +class Forward(ParseElementEnhance): + """Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. + + Note: take care when assigning to C{Forward} not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the C{Forward}:: + fwdExpr << (a | b | c) + Converting to use the '<<=' operator instead will avoid this problem. + """ + def __init__( self, other=None ): + super(Forward,self).__init__( other, savelist=False ) + + def __lshift__( self, other ): + if isinstance( other, basestring ): + other = ParserElement.literalStringClass(other) + self.expr = other + self.mayReturnEmpty = other.mayReturnEmpty + self.strRepr = None + self.mayIndexError = self.expr.mayIndexError + self.mayReturnEmpty = self.expr.mayReturnEmpty + self.setWhitespaceChars( self.expr.whiteChars ) + self.skipWhitespace = self.expr.skipWhitespace + self.saveAsList = self.expr.saveAsList + self.ignoreExprs.extend(self.expr.ignoreExprs) + return self + + def __ilshift__(self, other): + return self << other + + def leaveWhitespace( self ): + self.skipWhitespace = False + return self + + def streamline( self ): + if not self.streamlined: + self.streamlined = True + if self.expr is not None: + self.expr.streamline() + return self + + def validate( self, validateTrace=[] ): + if self not in validateTrace: + tmp = validateTrace[:]+[self] + if self.expr is not None: + self.expr.validate(tmp) + self.checkRecursion([]) + + def __str__( self ): + if hasattr(self,"name"): + return self.name + + self._revertClass = self.__class__ + self.__class__ = _ForwardNoRecurse + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + self.__class__ = self._revertClass + return self.__class__.__name__ + ": " + retString + + def copy(self): + if self.expr is not None: + return super(Forward,self).copy() + else: + ret = Forward() + ret <<= self + return ret + +class _ForwardNoRecurse(Forward): + def __str__( self ): + return "..." + +class TokenConverter(ParseElementEnhance): + """Abstract subclass of C{ParseExpression}, for converting parsed results.""" + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +class Upcase(TokenConverter): + """Converter to upper case all matching tokens.""" + def __init__(self, *args): + super(Upcase,self).__init__(*args) + warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", + DeprecationWarning,stacklevel=2) + + def postParse( self, instring, loc, tokenlist ): + return list(map( str.upper, tokenlist )) + + +class Combine(TokenConverter): + """Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying C{'adjacent=False'} in the constructor. + """ + def __init__( self, expr, joinString="", adjacent=True ): + super(Combine,self).__init__( expr ) + # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself + if adjacent: + self.leaveWhitespace() + self.adjacent = adjacent + self.skipWhitespace = True + self.joinString = joinString + self.callPreparse = True + + def ignore( self, other ): + if self.adjacent: + ParserElement.ignore(self, other) + else: + super( Combine, self).ignore( other ) + return self + + def postParse( self, instring, loc, tokenlist ): + retToks = tokenlist.copy() + del retToks[:] + retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults) + + if self.resultsName and retToks.haskeys(): + return [ retToks ] + else: + return retToks + +class Group(TokenConverter): + """Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.""" + def __init__( self, expr ): + super(Group,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + return [ tokenlist ] + +class Dict(TokenConverter): + """Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + """ + def __init__( self, expr ): + super(Dict,self).__init__( expr ) + self.saveAsList = True + + def postParse( self, instring, loc, tokenlist ): + for i,tok in enumerate(tokenlist): + if len(tok) == 0: + continue + ikey = tok[0] + if isinstance(ikey,int): + ikey = _ustr(tok[0]).strip() + if len(tok)==1: + tokenlist[ikey] = _ParseResultsWithOffset("",i) + elif len(tok)==2 and not isinstance(tok[1],ParseResults): + tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i) + else: + dictvalue = tok.copy() #ParseResults(i) + del dictvalue[0] + if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()): + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i) + else: + tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i) + + if self.resultsName: + return [ tokenlist ] + else: + return tokenlist + + +class Suppress(TokenConverter): + """Converter for ignoring the results of a parsed expression.""" + def postParse( self, instring, loc, tokenlist ): + return [] + + def suppress( self ): + return self + + +class OnlyOnce(object): + """Wrapper for parse actions, to ensure they are only called once.""" + def __init__(self, methodCall): + self.callable = _trim_arity(methodCall) + self.called = False + def __call__(self,s,l,t): + if not self.called: + results = self.callable(s,l,t) + self.called = True + return results + raise ParseException(s,l,"") + def reset(self): + self.called = False + +def traceParseAction(f): + """Decorator for debugging parse actions.""" + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.func_name + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) + try: + ret = f(*paArgs) + except Exception as exc: + sys.stderr.write( "<<leaving %s (exception: %s)\n" % (thisFunc,exc) ) + raise + sys.stderr.write( "<<leaving %s (ret: %s)\n" % (thisFunc,ret) ) + return ret + try: + z.__name__ = f.__name__ + except AttributeError: + pass + return z + +# +# global helpers +# +def delimitedList( expr, delim=",", combine=False ): + """Helper to define a delimited list of expressions - the delimiter defaults to ','. + By default, the list elements and delimiters can have intervening whitespace, and + comments, but this can be overridden by passing C{combine=True} in the constructor. + If C{combine} is set to C{True}, the matching tokens are returned as a single token + string, with the delimiters included; otherwise, the matching tokens are returned + as a list of tokens, with the delimiters suppressed. + """ + dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..." + if combine: + return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName) + else: + return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName) + +def countedArray( expr, intExpr=None ): + """Helper to define a counted list of expressions. + This helper defines a pattern of the form:: + integer expr expr expr... + where the leading integer tells how many expr expressions follow. + The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + """ + arrayExpr = Forward() + def countFieldParseAction(s,l,t): + n = t[0] + arrayExpr << (n and Group(And([expr]*n)) or Group(empty)) + return [] + if intExpr is None: + intExpr = Word(nums).setParseAction(lambda t:int(t[0])) + else: + intExpr = intExpr.copy() + intExpr.setName("arrayLen") + intExpr.addParseAction(countFieldParseAction, callDuringTry=True) + return ( intExpr + arrayExpr ) + +def _flatten(L): + ret = [] + for i in L: + if isinstance(i,list): + ret.extend(_flatten(i)) + else: + ret.append(i) + return ret + +def matchPreviousLiteral(expr): + """Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousLiteral(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches a + previous literal, will also match the leading C{"1:1"} in C{"1:10"}. + If this is not desired, use C{matchPreviousExpr}. + Do *not* use with packrat parsing enabled. + """ + rep = Forward() + def copyTokenToRepeater(s,l,t): + if t: + if len(t) == 1: + rep << t[0] + else: + # flatten t tokens + tflat = _flatten(t.asList()) + rep << And( [ Literal(tt) for tt in tflat ] ) + else: + rep << Empty() + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + return rep + +def matchPreviousExpr(expr): + """Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousExpr(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches by + expressions, will *not* match the leading C{"1:1"} in C{"1:10"}; + the expressions are evaluated first, and then compared, so + C{"1"} is compared with C{"10"}. + Do *not* use with packrat parsing enabled. + """ + rep = Forward() + e2 = expr.copy() + rep <<= e2 + def copyTokenToRepeater(s,l,t): + matchTokens = _flatten(t.asList()) + def mustMatchTheseTokens(s,l,t): + theseTokens = _flatten(t.asList()) + if theseTokens != matchTokens: + raise ParseException("",0,"") + rep.setParseAction( mustMatchTheseTokens, callDuringTry=True ) + expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + return rep + +def _escapeRegexRangeChars(s): + #~ escape these chars: ^-] + for c in r"\^-]": + s = s.replace(c,_bslash+c) + s = s.replace("\n",r"\n") + s = s.replace("\t",r"\t") + return _ustr(s) + +def oneOf( strs, caseless=False, useRegex=True ): + """Helper to quickly define a set of alternative Literals, and makes sure to do + longest-first testing when there is a conflict, regardless of the input order, + but returns a C{L{MatchFirst}} for best performance. + + Parameters: + - strs - a string of space-delimited literals, or a list of string literals + - caseless - (default=False) - treat all literals as caseless + - useRegex - (default=True) - as an optimization, will generate a Regex + object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or + if creating a C{Regex} raises an exception) + """ + if caseless: + isequal = ( lambda a,b: a.upper() == b.upper() ) + masks = ( lambda a,b: b.upper().startswith(a.upper()) ) + parseElementClass = CaselessLiteral + else: + isequal = ( lambda a,b: a == b ) + masks = ( lambda a,b: b.startswith(a) ) + parseElementClass = Literal + + symbols = [] + if isinstance(strs,basestring): + symbols = strs.split() + elif isinstance(strs, collections.Sequence): + symbols = list(strs[:]) + elif isinstance(strs, _generatorType): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or list", + SyntaxWarning, stacklevel=2) + if not symbols: + return NoMatch() + + i = 0 + while i < len(symbols)-1: + cur = symbols[i] + for j,other in enumerate(symbols[i+1:]): + if ( isequal(other, cur) ): + del symbols[i+j+1] + break + elif ( masks(cur, other) ): + del symbols[i+j+1] + symbols.insert(i,other) + cur = other + break + else: + i += 1 + + if not caseless and useRegex: + #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) + try: + if len(symbols)==len("".join(symbols)): + return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ) + except: + warnings.warn("Exception creating Regex for oneOf, building MatchFirst", + SyntaxWarning, stacklevel=2) + + + # last resort, just use MatchFirst + return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) + +def dictOf( key, value ): + """Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the C{Dict} results can include named token + fields. + """ + return Dict( ZeroOrMore( Group ( key + value ) ) ) + +def originalTextFor(expr, asString=True): + """Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. Simpler to use than the parse action C{L{keepOriginalText}}, and does not + require the inspect module to chase up the call stack. By default, returns a + string containing the original parsed text. + + If the optional C{asString} argument is passed as C{False}, then the return value is a + C{L{ParseResults}} containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to C{L{originalTextFor}} contains expressions with defined + results names, you must set C{asString} to C{False} if you want to preserve those + results name values.""" + locMarker = Empty().setParseAction(lambda s,loc,t: loc) + endlocMarker = locMarker.copy() + endlocMarker.callPreparse = False + matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") + if asString: + extractText = lambda s,l,t: s[t._original_start:t._original_end] + else: + def extractText(s,l,t): + del t[:] + t.insert(0, s[t._original_start:t._original_end]) + del t["_original_start"] + del t["_original_end"] + matchExpr.setParseAction(extractText) + return matchExpr + +def ungroup(expr): + """Helper to undo pyparsing's default grouping of And expressions, even + if all but one are non-empty.""" + return TokenConverter(expr).setParseAction(lambda t:t[0]) + +def locatedExpr(expr): + """Helper to decorate a returned token with its starting and ending locations in the input string. + This helper adds the following results names: + - locn_start = location where matched expression begins + - locn_end = location where matched expression ends + - value = the actual parsed results + + Be careful if the input text contains C{<TAB>} characters, you may want to call + C{L{ParserElement.parseWithTabs}} + """ + locator = Empty().setParseAction(lambda s,l,t: l) + return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) + + +# convenience constants for positional expressions +empty = Empty().setName("empty") +lineStart = LineStart().setName("lineStart") +lineEnd = LineEnd().setName("lineEnd") +stringStart = StringStart().setName("stringStart") +stringEnd = StringEnd().setName("stringEnd") + +_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1]) +_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16))) +_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8))) +_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(printables, excludeChars=r'\]', exact=1) | Regex(r"\w", re.UNICODE) +_charRange = Group(_singleChar + Suppress("-") + _singleChar) +_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" + +def srange(s): + r"""Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be:: + a single character + an escaped character with a leading backslash (such as \- or \]) + an escaped hex character with a leading '\x' (\x21, which is a '!' character) + (\0x## is also supported for backwards compatibility) + an escaped octal character with a leading '\0' (\041, which is a '!' character) + a range of any of the above, separated by a dash ('a-z', etc.) + any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) + """ + _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1)) + try: + return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body) + except: + return "" + +def matchOnlyAtCol(n): + """Helper method for defining parse actions that require matching at a specific + column in the input text. + """ + def verifyCol(strg,locn,toks): + if col(locn,strg) != n: + raise ParseException(strg,locn,"matched token not at column %d" % n) + return verifyCol + +def replaceWith(replStr): + """Helper method for common parse actions that simply return a literal value. Especially + useful when used with C{L{transformString<ParserElement.transformString>}()}. + """ + #def _replFunc(*args): + # return [replStr] + #return _replFunc + return functools.partial(next, itertools.repeat([replStr])) + +def removeQuotes(s,l,t): + """Helper parse action for removing quotation marks from parsed quoted strings. + To use, add this parse action to quoted string using:: + quotedString.setParseAction( removeQuotes ) + """ + return t[0][1:-1] + +def upcaseTokens(s,l,t): + """Helper parse action to convert tokens to upper case.""" + return [ tt.upper() for tt in map(_ustr,t) ] + +def downcaseTokens(s,l,t): + """Helper parse action to convert tokens to lower case.""" + return [ tt.lower() for tt in map(_ustr,t) ] + +def keepOriginalText(s,startLoc,t): + """DEPRECATED - use new helper method C{L{originalTextFor}}. + Helper parse action to preserve original parsed text, + overriding any nested parse actions.""" + try: + endloc = getTokensEndLoc() + except ParseException: + raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") + del t[:] + t += ParseResults(s[startLoc:endloc]) + return t + +def getTokensEndLoc(): + """Method to be called from within a parse action to determine the end + location of the parsed tokens.""" + import inspect + fstack = inspect.stack() + try: + # search up the stack (through intervening argument normalizers) for correct calling routine + for f in fstack[2:]: + if f[3] == "_parseNoCache": + endloc = f[0].f_locals["loc"] + return endloc + else: + raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") + finally: + del fstack + +def _makeTags(tagStr, xml): + """Internal helper to construct opening and closing tag expressions, given a tag name""" + if isinstance(tagStr,basestring): + resname = tagStr + tagStr = Keyword(tagStr, caseless=not xml) + else: + resname = tagStr.name + + tagAttrName = Word(alphas,alphanums+"_-:") + if (xml): + tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes ) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + else: + printablesLessRAbrack = "".join(c for c in printables if c not in ">") + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack) + openTag = Suppress("<") + tagStr("tag") + \ + Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \ + Optional( Suppress("=") + tagAttrValue ) ))) + \ + Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") + closeTag = Combine(_L("</") + tagStr + ">") + + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % tagStr) + openTag.tag = resname + closeTag.tag = resname + return openTag, closeTag + +def makeHTMLTags(tagStr): + """Helper to construct opening and closing tag expressions for HTML, given a tag name""" + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """Helper to construct opening and closing tag expressions for XML, given a tag name""" + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """Helper to create a validating parse action to be used with start tags created + with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + C{<TD>} or C{<DIV>}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted) + - lpar - expression for matching left-parentheses (default=Suppress('(')) + - rpar - expression for matching right-parentheses (default=Suppress(')')) + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward()#.setName("expr%d" % i) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + matchExpr.setParseAction( pa ) + thisExpr <<= ( matchExpr | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret +operatorPrecedence = infixNotation + +dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") +sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") +quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()) + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default="("); can also be a pyparsing expression + - closer - closing character for a nested list (default=")"); can also be a pyparsing expression + - content - expression for items within the nested lists (default=None) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=True) + + A valid block must contain at least one C{blockStatement}. + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = Empty() + Empty().setParseAction(checkSubIndent) + PEER = Empty().setParseAction(checkPeerIndent) + UNDENT = Empty().setParseAction(checkUnindent) + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) +commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() +_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) +replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") + +htmlComment = Regex(r"<!--[\s\S]*?-->") +restOfLine = Regex(r".*").leaveWhitespace() +dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") +cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?<!\\)|\Z))").setName("C++ style comment") + +javaStyleComment = cppStyleComment +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") + + +if __name__ == "__main__": + + selectToken = CaselessLiteral( "select" ) + fromToken = CaselessLiteral( "from" ) + + ident = Word( alphas, alphanums + "_$" ) + columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) + columnNameList = Group( delimitedList( columnName ) ).setName("columns") + tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) + tableNameList = Group( delimitedList( tableName ) ).setName("tables") + simpleSQL = ( selectToken + \ + ( '*' | columnNameList ).setResultsName( "columns" ) + \ + fromToken + \ + tableNameList.setResultsName( "tables" ) ) + + simpleSQL.runTests("""\ + SELECT * from XYZZY, ABC + select * from SYS.XYZZY + Select A from Sys.dual + Select AA,BB,CC from Sys.dual + Select A, B, C from Sys.dual + Select A, B, C from Sys.dual + Xelect A, B, C from Sys.dual + Select A, B, C frox Sys.dual + Select + Select ^^^ frox Sys.dual + Select A, B, C from Sys.dual, Table2""") + diff --git a/venv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/lib/python3.5/site-packages/pkg_resources/extern/__init__.py b/venv/lib/python3.5/site-packages/pkg_resources/extern/__init__.py new file mode 100644 index 0000000..6758d36 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pkg_resources/extern/__init__.py @@ -0,0 +1,71 @@ +import sys + + +class VendorImporter: + """ + A PEP 302 meta path importer for finding optionally-vendored + or otherwise naturally-installed packages from root_name. + """ + def __init__(self, root_name, vendored_names=(), vendor_pkg=None): + self.root_name = root_name + self.vendored_names = set(vendored_names) + self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') + + @property + def search_path(self): + """ + Search first the vendor package then as a natural package. + """ + yield self.vendor_pkg + '.' + yield '' + + def find_module(self, fullname, path=None): + """ + Return self when fullname starts with root_name and the + target module is one vendored through this importer. + """ + root, base, target = fullname.partition(self.root_name + '.') + if root: + return + if not any(map(target.startswith, self.vendored_names)): + return + return self + + def load_module(self, fullname): + """ + Iterate over the search path to locate and load fullname. + """ + root, base, target = fullname.partition(self.root_name + '.') + for prefix in self.search_path: + try: + extant = prefix + target + __import__(extant) + mod = sys.modules[extant] + sys.modules[fullname] = mod + # mysterious hack: + # Remove the reference to the extant package/module + # on later Python versions to cause relative imports + # in the vendor package to resolve the same modules + # as those going through this importer. + if sys.version_info > (3, 3): + del sys.modules[extant] + return mod + except ImportError: + pass + else: + raise ImportError( + "The '{target}' package is required; " + "normally this is bundled with this package so if you get " + "this warning, consult the packager of your " + "distribution.".format(**locals()) + ) + + def install(self): + """ + Install this importer into sys.meta_path if not already present. + """ + if self not in sys.meta_path: + sys.meta_path.append(self) + +names = 'packaging', 'pyparsing', 'six' +VendorImporter(__name__, names).install() diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..d2a2562 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/DESCRIPTION.rst @@ -0,0 +1,23 @@ +.. image:: https://img.shields.io/pypi/pyversions/pytest.svg + :target: https://pypi.org/project/py +.. image:: https://img.shields.io/travis/pytest-dev/py.svg + :target: https://travis-ci.org/pytest-dev/py + +The py lib is a Python development support library featuring +the following tools and modules: + +* ``py.path``: uniform local and svn path objects +* ``py.apipkg``: explicit API control and lazy-importing +* ``py.iniconfig``: easy parsing of .ini files +* ``py.code``: dynamic code generation and introspection + +NOTE: prior to the 1.4 release this distribution used to +contain py.test which is now its own package, see http://pytest.org + +For questions and more information please visit http://py.readthedocs.org + +Bugs and issues: https://github.com/pytest-dev/py + +Authors: Holger Krekel and others, 2004-2017 + + diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/LICENSE.txt b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/LICENSE.txt new file mode 100644 index 0000000..790c261 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/LICENSE.txt @@ -0,0 +1,19 @@ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/METADATA b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/METADATA new file mode 100644 index 0000000..6bb4c3a --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/METADATA @@ -0,0 +1,48 @@ +Metadata-Version: 2.0 +Name: py +Version: 1.4.34 +Summary: library with cross-python path, ini-parsing, io, code, log facilities +Home-page: http://py.readthedocs.io/ +Author: holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others +Author-email: pytest-dev@python.org +License: MIT license +Platform: unix +Platform: linux +Platform: osx +Platform: cygwin +Platform: win32 +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Topic :: Software Development :: Testing +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 + +.. image:: https://img.shields.io/pypi/pyversions/pytest.svg + :target: https://pypi.org/project/py +.. image:: https://img.shields.io/travis/pytest-dev/py.svg + :target: https://travis-ci.org/pytest-dev/py + +The py lib is a Python development support library featuring +the following tools and modules: + +* ``py.path``: uniform local and svn path objects +* ``py.apipkg``: explicit API control and lazy-importing +* ``py.iniconfig``: easy parsing of .ini files +* ``py.code``: dynamic code generation and introspection + +NOTE: prior to the 1.4 release this distribution used to +contain py.test which is now its own package, see http://pytest.org + +For questions and more information please visit http://py.readthedocs.org + +Bugs and issues: https://github.com/pytest-dev/py + +Authors: Holger Krekel and others, 2004-2017 + + diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/RECORD b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/RECORD new file mode 100644 index 0000000..627ccca --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/RECORD @@ -0,0 +1,74 @@ +py/__init__.py,sha256=5J0eNhSXu47etaQMA46OQGCT8txvP2UZirz92LoZgRQ,6080 +py/__metainfo.py,sha256=pFMfAGHL6CyKiBq8B04_cWYsy3NTT0Zni0zkEDKCcI4,57 +py/_apipkg.py,sha256=Ei3D8e5ejPQWBfxjHNFX95D7B1ZGug1zL8W42FR1-_E,6036 +py/_builtin.py,sha256=7o0W5kXt4_61w7OWYx_-iUmm9xSnFAnzaoMOp7QXZEc,6769 +py/_error.py,sha256=e5KUTfCaRJNP_g4jqR1X4Q3oS8FBFI-QpcyErV9j4eI,2928 +py/_iniconfig.py,sha256=1nQ-98BJOBOHGkp-mIGGABWVXVg_jYQ4Lz-_WQRlt_Q,5355 +py/_std.py,sha256=un9XhDfAgY3bYbnuYwK-L2iC0lLZ95Qqh-ss_e7Cvm4,433 +py/_xmlgen.py,sha256=5qTsQ8g531nIl3lOA4q80YbEZcCLVWQOCsezRplYlOs,8619 +py/test.py,sha256=q-TVyj_8W-ZB6d1gp_CRjZyqvSYV2foxQRbcUoLjJJw,232 +py/_code/__init__.py,sha256=DQ0hvCWtOfRLxmV6FnAWycsvnoY6dnfwEFMy73S8kYg,47 +py/_code/_assertionnew.py,sha256=1Ix0P-QhrmmK8-qqBChgiJqFLlI_4Ekgv29vxkljHdw,12723 +py/_code/_assertionold.py,sha256=5wmmUaJtZ43sM1_8VT0un2At2NsXKT24bOXh92zErg4,18418 +py/_code/_py2traceback.py,sha256=vb-2kfq-StMaG2w6bfw3mPifcmF7pn4uRl1rgENgn0I,2844 +py/_code/assertion.py,sha256=SwhL9xhOf7FspLTHRht6AJp2NIC8gzg1pvE90RjyXIY,3381 +py/_code/code.py,sha256=tJ_KbpwhNMN9Z5AQNm6JzfsUbo0YJiFmDFLFv7MQVVw,28228 +py/_code/source.py,sha256=HbcZkCBYFhIBw1rIShtuYrQ6kSBcc4JVFStGTzk7Ifc,14525 +py/_io/__init__.py,sha256=TdnZK_eUMWsEURzXWVmTnOMixTHimK6yHuUS9On88DM,30 +py/_io/capture.py,sha256=6qIK9HoVRrfI6VJS61280FVNezNhFrtzp_tPhk20HjE,12011 +py/_io/saferepr.py,sha256=L37QomdlZnXqE208xRCi1yiJ_vsT0POoY-IyYg5c1a4,2554 +py/_io/terminalwriter.py,sha256=YP7FFA4f8jdosCt0INiKNHVer17S1eByV4mhD6AZCLw,13106 +py/_log/__init__.py,sha256=rxgcYPMS-fOXRaOQIJRDqEO3ziyW-91NhFokzdDkb_M,76 +py/_log/log.py,sha256=PnEto-RGnNYZJ9QdOThG_fxhtnQOqjI3KkH2xt95A9E,6081 +py/_log/warning.py,sha256=7VeifGAU2GgcLbU9lQwO7yu7JHA-JepFXaJx656MMcY,2618 +py/_path/__init__.py,sha256=1dMOH9zuZQV4_Q2kMOHtjA4heUqM2gVDDSOJ_7vTfNQ,33 +py/_path/cacheutil.py,sha256=gWwxwfUCQWGgFPT9tsMaz-uomjiRparhxO84HzSv6ok,3447 +py/_path/common.py,sha256=CPZMuH3ftV8WDHvGJU_HBMly9-3upWmKB6RgRg3e8RU,14844 +py/_path/local.py,sha256=howBO3uiN37OIYNChvtRZf1XGobggKtxA4460WC0FZI,33568 +py/_path/svnurl.py,sha256=w_hTOYIgOEIKe7k4EfKTrT6ns0mQROm0jbtEb8FvHpo,15095 +py/_path/svnwc.py,sha256=_YN4XmbFeox5PqiASV45iHTToOvjLpBjU9A7_r76NRo,45089 +py/_process/__init__.py,sha256=7wR_PgdLZx16epAJB09JOhWw-RAZBlOaSy_OctdsD4c,41 +py/_process/cmdexec.py,sha256=unIPyZXQcGF_wHZBZncmQhSSR-aaiOWESYsxWOD35qU,1863 +py/_process/forkedfunc.py,sha256=SaLmnA3z3eKftQ4vrNUkGUZJb3fY29OJvB3pB_I-z8c,3812 +py/_process/killproc.py,sha256=vNcOjh7eTRKZMd5BzwGJGb6eqrk8HgzXLDB13SZm05c,671 +py-1.4.34.dist-info/DESCRIPTION.rst,sha256=6tYfJwotn446mSdFV5pnM2hF4e01sxroGXloz0-a3sg,817 +py-1.4.34.dist-info/LICENSE.txt,sha256=lzT2iwmQMhJkdHxOoJR_hS0kgPQRX2RJzjv7_aF32OM,1080 +py-1.4.34.dist-info/METADATA,sha256=5iQQQWCijdAETfcmqbu471UixH7vrx1F5Vij97daKos,1698 +py-1.4.34.dist-info/RECORD,, +py-1.4.34.dist-info/WHEEL,sha256=5f-Lb0dq-Ei5P75pgK065AnrFl2dK_ZqskZBpCbWERA,116 +py-1.4.34.dist-info/metadata.json,sha256=feWylPGwn_KxDvtVimq51RAQd8uH9Go2cE7GoavlQDI,971 +py-1.4.34.dist-info/top_level.txt,sha256=rwh8_ukTaGscjyhGkBVcsGOMdc-Cfdz2QH7BKGENv-4,3 +py-1.4.34.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +py/__pycache__/_builtin.cpython-35.pyc,, +py/_io/__pycache__/capture.cpython-35.pyc,, +py/_path/__pycache__/common.cpython-35.pyc,, +py/__pycache__/_std.cpython-35.pyc,, +py/_io/__pycache__/terminalwriter.cpython-35.pyc,, +py/_log/__pycache__/__init__.cpython-35.pyc,, +py/_path/__pycache__/cacheutil.cpython-35.pyc,, +py/_code/__pycache__/assertion.cpython-35.pyc,, +py/__pycache__/_iniconfig.cpython-35.pyc,, +py/_log/__pycache__/log.cpython-35.pyc,, +py/_code/__pycache__/__init__.cpython-35.pyc,, +py/_log/__pycache__/warning.cpython-35.pyc,, +py/__pycache__/test.cpython-35.pyc,, +py/_process/__pycache__/killproc.cpython-35.pyc,, +py/_process/__pycache__/__init__.cpython-35.pyc,, +py/__pycache__/_error.cpython-35.pyc,, +py/__pycache__/__metainfo.cpython-35.pyc,, +py/_code/__pycache__/source.cpython-35.pyc,, +py/_path/__pycache__/local.cpython-35.pyc,, +py/_code/__pycache__/_assertionnew.cpython-35.pyc,, +py/_process/__pycache__/forkedfunc.cpython-35.pyc,, +py/_path/__pycache__/svnurl.cpython-35.pyc,, +py/__pycache__/_apipkg.cpython-35.pyc,, +py/_code/__pycache__/_py2traceback.cpython-35.pyc,, +py/_code/__pycache__/_assertionold.cpython-35.pyc,, +py/__pycache__/_xmlgen.cpython-35.pyc,, +py/_io/__pycache__/__init__.cpython-35.pyc,, +py/_code/__pycache__/code.cpython-35.pyc,, +py/_io/__pycache__/saferepr.cpython-35.pyc,, +py/_path/__pycache__/__init__.cpython-35.pyc,, +py/_process/__pycache__/cmdexec.cpython-35.pyc,, +py/__pycache__/__init__.cpython-35.pyc,, +py/_path/__pycache__/svnwc.cpython-35.pyc,, diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/WHEEL b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/WHEEL new file mode 100644 index 0000000..4d519f1 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.26.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/metadata.json b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/metadata.json new file mode 100644 index 0000000..cd3b8a8 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/metadata.json @@ -0,0 +1 @@ +{"generator": "bdist_wheel (0.26.0)", "summary": "library with cross-python path, ini-parsing, io, code, log facilities", "classifiers": ["Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"project_urls": {"Home": "http://py.readthedocs.io/"}, "contacts": [{"email": "pytest-dev@python.org", "name": "holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}}}, "license": "MIT license", "metadata_version": "2.0", "name": "py", "platform": "unix", "version": "1.4.34"} \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/top_level.txt new file mode 100644 index 0000000..edfce78 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py-1.4.34.dist-info/top_level.txt @@ -0,0 +1 @@ +py diff --git a/venv/lib/python3.5/site-packages/py/__init__.py b/venv/lib/python3.5/site-packages/py/__init__.py new file mode 100644 index 0000000..4a2b6fa --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/__init__.py @@ -0,0 +1,152 @@ +""" +pylib: rapid testing and development utils + +this module uses apipkg.py for lazy-loading sub modules +and classes. The initpkg-dictionary below specifies +name->value mappings where value can be another namespace +dictionary or an import path. + +(c) Holger Krekel and others, 2004-2014 +""" +__version__ = '1.4.34' + +from py import _apipkg + +# so that py.error.* instances are picklable +import sys +sys.modules['py.error'] = _apipkg.AliasModule("py.error", "py._error", 'error') +import py.error # "Dereference" it now just to be safe (issue110) + + +_apipkg.initpkg(__name__, attr={'_apipkg': _apipkg}, exportdefs={ + # access to all standard lib modules + 'std': '._std:std', + # access to all posix errno's as classes + 'error': '._error:error', + + '_pydir' : '.__metainfo:pydir', + 'version': 'py:__version__', # backward compatibility + + # pytest-2.0 has a flat namespace, we use alias modules + # to keep old references compatible + 'test' : 'pytest', + 'test.collect' : 'pytest', + 'test.cmdline' : 'pytest', + + # hook into the top-level standard library + 'process' : { + '__doc__' : '._process:__doc__', + 'cmdexec' : '._process.cmdexec:cmdexec', + 'kill' : '._process.killproc:kill', + 'ForkedFunc' : '._process.forkedfunc:ForkedFunc', + }, + + 'apipkg' : { + 'initpkg' : '._apipkg:initpkg', + 'ApiModule' : '._apipkg:ApiModule', + }, + + 'iniconfig' : { + 'IniConfig' : '._iniconfig:IniConfig', + 'ParseError' : '._iniconfig:ParseError', + }, + + 'path' : { + '__doc__' : '._path:__doc__', + 'svnwc' : '._path.svnwc:SvnWCCommandPath', + 'svnurl' : '._path.svnurl:SvnCommandPath', + 'local' : '._path.local:LocalPath', + 'SvnAuth' : '._path.svnwc:SvnAuth', + }, + + # python inspection/code-generation API + 'code' : { + '__doc__' : '._code:__doc__', + 'compile' : '._code.source:compile_', + 'Source' : '._code.source:Source', + 'Code' : '._code.code:Code', + 'Frame' : '._code.code:Frame', + 'ExceptionInfo' : '._code.code:ExceptionInfo', + 'Traceback' : '._code.code:Traceback', + 'getfslineno' : '._code.source:getfslineno', + 'getrawcode' : '._code.code:getrawcode', + 'patch_builtins' : '._code.code:patch_builtins', + 'unpatch_builtins' : '._code.code:unpatch_builtins', + '_AssertionError' : '._code.assertion:AssertionError', + '_reinterpret_old' : '._code.assertion:reinterpret_old', + '_reinterpret' : '._code.assertion:reinterpret', + '_reprcompare' : '._code.assertion:_reprcompare', + '_format_explanation' : '._code.assertion:_format_explanation', + }, + + # backports and additions of builtins + 'builtin' : { + '__doc__' : '._builtin:__doc__', + 'enumerate' : '._builtin:enumerate', + 'reversed' : '._builtin:reversed', + 'sorted' : '._builtin:sorted', + 'any' : '._builtin:any', + 'all' : '._builtin:all', + 'set' : '._builtin:set', + 'frozenset' : '._builtin:frozenset', + 'BaseException' : '._builtin:BaseException', + 'GeneratorExit' : '._builtin:GeneratorExit', + '_sysex' : '._builtin:_sysex', + 'print_' : '._builtin:print_', + '_reraise' : '._builtin:_reraise', + '_tryimport' : '._builtin:_tryimport', + 'exec_' : '._builtin:exec_', + '_basestring' : '._builtin:_basestring', + '_totext' : '._builtin:_totext', + '_isbytes' : '._builtin:_isbytes', + '_istext' : '._builtin:_istext', + '_getimself' : '._builtin:_getimself', + '_getfuncdict' : '._builtin:_getfuncdict', + '_getcode' : '._builtin:_getcode', + 'builtins' : '._builtin:builtins', + 'execfile' : '._builtin:execfile', + 'callable' : '._builtin:callable', + 'bytes' : '._builtin:bytes', + 'text' : '._builtin:text', + }, + + # input-output helping + 'io' : { + '__doc__' : '._io:__doc__', + 'dupfile' : '._io.capture:dupfile', + 'TextIO' : '._io.capture:TextIO', + 'BytesIO' : '._io.capture:BytesIO', + 'FDCapture' : '._io.capture:FDCapture', + 'StdCapture' : '._io.capture:StdCapture', + 'StdCaptureFD' : '._io.capture:StdCaptureFD', + 'TerminalWriter' : '._io.terminalwriter:TerminalWriter', + 'ansi_print' : '._io.terminalwriter:ansi_print', + 'get_terminal_width' : '._io.terminalwriter:get_terminal_width', + 'saferepr' : '._io.saferepr:saferepr', + }, + + # small and mean xml/html generation + 'xml' : { + '__doc__' : '._xmlgen:__doc__', + 'html' : '._xmlgen:html', + 'Tag' : '._xmlgen:Tag', + 'raw' : '._xmlgen:raw', + 'Namespace' : '._xmlgen:Namespace', + 'escape' : '._xmlgen:escape', + }, + + 'log' : { + # logging API ('producers' and 'consumers' connected via keywords) + '__doc__' : '._log:__doc__', + '_apiwarn' : '._log.warning:_apiwarn', + 'Producer' : '._log.log:Producer', + 'setconsumer' : '._log.log:setconsumer', + '_setstate' : '._log.log:setstate', + '_getstate' : '._log.log:getstate', + 'Path' : '._log.log:Path', + 'STDOUT' : '._log.log:STDOUT', + 'STDERR' : '._log.log:STDERR', + 'Syslog' : '._log.log:Syslog', + }, + +}) diff --git a/venv/lib/python3.5/site-packages/py/__metainfo.py b/venv/lib/python3.5/site-packages/py/__metainfo.py new file mode 100644 index 0000000..067806c --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/__metainfo.py @@ -0,0 +1,2 @@ +import py +pydir = py.path.local(py.__file__).dirpath() diff --git a/venv/lib/python3.5/site-packages/py/_apipkg.py b/venv/lib/python3.5/site-packages/py/_apipkg.py new file mode 100644 index 0000000..42bd29b --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_apipkg.py @@ -0,0 +1,181 @@ +""" +apipkg: control the exported namespace of a python package. + +see http://pypi.python.org/pypi/apipkg + +(c) holger krekel, 2009 - MIT license +""" +import os +import sys +from types import ModuleType + +__version__ = '1.3.dev' + +def _py_abspath(path): + """ + special version of abspath + that will leave paths from jython jars alone + """ + if path.startswith('__pyclasspath__'): + + return path + else: + return os.path.abspath(path) + +def initpkg(pkgname, exportdefs, attr=dict()): + """ initialize given package from the export definitions. """ + oldmod = sys.modules.get(pkgname) + d = {} + f = getattr(oldmod, '__file__', None) + if f: + f = _py_abspath(f) + d['__file__'] = f + if hasattr(oldmod, '__version__'): + d['__version__'] = oldmod.__version__ + if hasattr(oldmod, '__loader__'): + d['__loader__'] = oldmod.__loader__ + if hasattr(oldmod, '__path__'): + d['__path__'] = [_py_abspath(p) for p in oldmod.__path__] + if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None): + d['__doc__'] = oldmod.__doc__ + d.update(attr) + if hasattr(oldmod, "__dict__"): + oldmod.__dict__.update(d) + mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d) + sys.modules[pkgname] = mod + +def importobj(modpath, attrname): + module = __import__(modpath, None, None, ['__doc__']) + if not attrname: + return module + + retval = module + names = attrname.split(".") + for x in names: + retval = getattr(retval, x) + return retval + +class ApiModule(ModuleType): + def __docget(self): + try: + return self.__doc + except AttributeError: + if '__doc__' in self.__map__: + return self.__makeattr('__doc__') + def __docset(self, value): + self.__doc = value + __doc__ = property(__docget, __docset) + + def __init__(self, name, importspec, implprefix=None, attr=None): + self.__name__ = name + self.__all__ = [x for x in importspec if x != '__onfirstaccess__'] + self.__map__ = {} + self.__implprefix__ = implprefix or name + if attr: + for name, val in attr.items(): + # print "setting", self.__name__, name, val + setattr(self, name, val) + for name, importspec in importspec.items(): + if isinstance(importspec, dict): + subname = '%s.%s' % (self.__name__, name) + apimod = ApiModule(subname, importspec, implprefix) + sys.modules[subname] = apimod + setattr(self, name, apimod) + else: + parts = importspec.split(':') + modpath = parts.pop(0) + attrname = parts and parts[0] or "" + if modpath[0] == '.': + modpath = implprefix + modpath + + if not attrname: + subname = '%s.%s' % (self.__name__, name) + apimod = AliasModule(subname, modpath) + sys.modules[subname] = apimod + if '.' not in name: + setattr(self, name, apimod) + else: + self.__map__[name] = (modpath, attrname) + + def __repr__(self): + l = [] + if hasattr(self, '__version__'): + l.append("version=" + repr(self.__version__)) + if hasattr(self, '__file__'): + l.append('from ' + repr(self.__file__)) + if l: + return '<ApiModule %r %s>' % (self.__name__, " ".join(l)) + return '<ApiModule %r>' % (self.__name__,) + + def __makeattr(self, name): + """lazily compute value for name or raise AttributeError if unknown.""" + # print "makeattr", self.__name__, name + target = None + if '__onfirstaccess__' in self.__map__: + target = self.__map__.pop('__onfirstaccess__') + importobj(*target)() + try: + modpath, attrname = self.__map__[name] + except KeyError: + if target is not None and name != '__onfirstaccess__': + # retry, onfirstaccess might have set attrs + return getattr(self, name) + raise AttributeError(name) + else: + result = importobj(modpath, attrname) + setattr(self, name, result) + try: + del self.__map__[name] + except KeyError: + pass # in a recursive-import situation a double-del can happen + return result + + __getattr__ = __makeattr + + def __dict__(self): + # force all the content of the module to be loaded when __dict__ is read + dictdescr = ModuleType.__dict__['__dict__'] + dict = dictdescr.__get__(self) + if dict is not None: + hasattr(self, 'some') + for name in self.__all__: + try: + self.__makeattr(name) + except AttributeError: + pass + return dict + __dict__ = property(__dict__) + + +def AliasModule(modname, modpath, attrname=None): + mod = [] + + def getmod(): + if not mod: + x = importobj(modpath, None) + if attrname is not None: + x = getattr(x, attrname) + mod.append(x) + return mod[0] + + class AliasModule(ModuleType): + + def __repr__(self): + x = modpath + if attrname: + x += "." + attrname + return '<AliasModule %r for %r>' % (modname, x) + + def __getattribute__(self, name): + try: + return getattr(getmod(), name) + except ImportError: + return None + + def __setattr__(self, name, value): + setattr(getmod(), name, value) + + def __delattr__(self, name): + delattr(getmod(), name) + + return AliasModule(str(modname)) diff --git a/venv/lib/python3.5/site-packages/py/_builtin.py b/venv/lib/python3.5/site-packages/py/_builtin.py new file mode 100644 index 0000000..6199afa --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_builtin.py @@ -0,0 +1,248 @@ +import sys + +try: + reversed = reversed +except NameError: + def reversed(sequence): + """reversed(sequence) -> reverse iterator over values of the sequence + + Return a reverse iterator + """ + if hasattr(sequence, '__reversed__'): + return sequence.__reversed__() + if not hasattr(sequence, '__getitem__'): + raise TypeError("argument to reversed() must be a sequence") + return reversed_iterator(sequence) + + class reversed_iterator(object): + + def __init__(self, seq): + self.seq = seq + self.remaining = len(seq) + + def __iter__(self): + return self + + def next(self): + i = self.remaining + if i > 0: + i -= 1 + item = self.seq[i] + self.remaining = i + return item + raise StopIteration + + def __length_hint__(self): + return self.remaining + +try: + any = any +except NameError: + def any(iterable): + for x in iterable: + if x: + return True + return False + +try: + all = all +except NameError: + def all(iterable): + for x in iterable: + if not x: + return False + return True + +try: + sorted = sorted +except NameError: + builtin_cmp = cmp # need to use cmp as keyword arg + + def sorted(iterable, cmp=None, key=None, reverse=0): + use_cmp = None + if key is not None: + if cmp is None: + def use_cmp(x, y): + return builtin_cmp(x[0], y[0]) + else: + def use_cmp(x, y): + return cmp(x[0], y[0]) + l = [(key(element), element) for element in iterable] + else: + if cmp is not None: + use_cmp = cmp + l = list(iterable) + if use_cmp is not None: + l.sort(use_cmp) + else: + l.sort() + if reverse: + l.reverse() + if key is not None: + return [element for (_, element) in l] + return l + +try: + set, frozenset = set, frozenset +except NameError: + from sets import set, frozenset + +# pass through +enumerate = enumerate + +try: + BaseException = BaseException +except NameError: + BaseException = Exception + +try: + GeneratorExit = GeneratorExit +except NameError: + class GeneratorExit(Exception): + """ This exception is never raised, it is there to make it possible to + write code compatible with CPython 2.5 even in lower CPython + versions.""" + pass + GeneratorExit.__module__ = 'exceptions' + +_sysex = (KeyboardInterrupt, SystemExit, MemoryError, GeneratorExit) + +try: + callable = callable +except NameError: + def callable(obj): + return hasattr(obj, "__call__") + +if sys.version_info >= (3, 0): + exec ("print_ = print ; exec_=exec") + import builtins + + # some backward compatibility helpers + _basestring = str + def _totext(obj, encoding=None, errors=None): + if isinstance(obj, bytes): + if errors is None: + obj = obj.decode(encoding) + else: + obj = obj.decode(encoding, errors) + elif not isinstance(obj, str): + obj = str(obj) + return obj + + def _isbytes(x): + return isinstance(x, bytes) + def _istext(x): + return isinstance(x, str) + + text = str + bytes = bytes + + + def _getimself(function): + return getattr(function, '__self__', None) + + def _getfuncdict(function): + return getattr(function, "__dict__", None) + + def _getcode(function): + return getattr(function, "__code__", None) + + def execfile(fn, globs=None, locs=None): + if globs is None: + back = sys._getframe(1) + globs = back.f_globals + locs = back.f_locals + del back + elif locs is None: + locs = globs + fp = open(fn, "r") + try: + source = fp.read() + finally: + fp.close() + co = compile(source, fn, "exec", dont_inherit=True) + exec_(co, globs, locs) + +else: + import __builtin__ as builtins + _totext = unicode + _basestring = basestring + text = unicode + bytes = str + execfile = execfile + callable = callable + def _isbytes(x): + return isinstance(x, str) + def _istext(x): + return isinstance(x, unicode) + + def _getimself(function): + return getattr(function, 'im_self', None) + + def _getfuncdict(function): + return getattr(function, "__dict__", None) + + def _getcode(function): + try: + return getattr(function, "__code__") + except AttributeError: + return getattr(function, "func_code", None) + + def print_(*args, **kwargs): + """ minimal backport of py3k print statement. """ + sep = ' ' + if 'sep' in kwargs: + sep = kwargs.pop('sep') + end = '\n' + if 'end' in kwargs: + end = kwargs.pop('end') + file = 'file' in kwargs and kwargs.pop('file') or sys.stdout + if kwargs: + args = ", ".join([str(x) for x in kwargs]) + raise TypeError("invalid keyword arguments: %s" % args) + at_start = True + for x in args: + if not at_start: + file.write(sep) + file.write(str(x)) + at_start = False + file.write(end) + + def exec_(obj, globals=None, locals=None): + """ minimal backport of py3k exec statement. """ + __tracebackhide__ = True + if globals is None: + frame = sys._getframe(1) + globals = frame.f_globals + if locals is None: + locals = frame.f_locals + elif locals is None: + locals = globals + exec2(obj, globals, locals) + +if sys.version_info >= (3, 0): + def _reraise(cls, val, tb): + __tracebackhide__ = True + assert hasattr(val, '__traceback__') + raise cls.with_traceback(val, tb) +else: + exec (""" +def _reraise(cls, val, tb): + __tracebackhide__ = True + raise cls, val, tb +def exec2(obj, globals, locals): + __tracebackhide__ = True + exec obj in globals, locals +""") + +def _tryimport(*names): + """ return the first successfully imported module. """ + assert names + for name in names: + try: + __import__(name) + except ImportError: + excinfo = sys.exc_info() + else: + return sys.modules[name] + _reraise(*excinfo) diff --git a/venv/lib/python3.5/site-packages/py/_code/__init__.py b/venv/lib/python3.5/site-packages/py/_code/__init__.py new file mode 100644 index 0000000..152ff58 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/__init__.py @@ -0,0 +1 @@ +""" python inspection/code generation API """ diff --git a/venv/lib/python3.5/site-packages/py/_code/_assertionnew.py b/venv/lib/python3.5/site-packages/py/_code/_assertionnew.py new file mode 100644 index 0000000..e9b7050 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/_assertionnew.py @@ -0,0 +1,339 @@ +""" +Find intermediate evalutation results in assert statements through builtin AST. +This should replace _assertionold.py eventually. +""" + +import sys +import ast + +import py +from py._code.assertion import _format_explanation, BuiltinAssertionError + + +if sys.platform.startswith("java") and sys.version_info < (2, 5, 2): + # See http://bugs.jython.org/issue1497 + _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict", + "ListComp", "GeneratorExp", "Yield", "Compare", "Call", + "Repr", "Num", "Str", "Attribute", "Subscript", "Name", + "List", "Tuple") + _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign", + "AugAssign", "Print", "For", "While", "If", "With", "Raise", + "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom", + "Exec", "Global", "Expr", "Pass", "Break", "Continue") + _expr_nodes = set(getattr(ast, name) for name in _exprs) + _stmt_nodes = set(getattr(ast, name) for name in _stmts) + def _is_ast_expr(node): + return node.__class__ in _expr_nodes + def _is_ast_stmt(node): + return node.__class__ in _stmt_nodes +else: + def _is_ast_expr(node): + return isinstance(node, ast.expr) + def _is_ast_stmt(node): + return isinstance(node, ast.stmt) + + +class Failure(Exception): + """Error found while interpreting AST.""" + + def __init__(self, explanation=""): + self.cause = sys.exc_info() + self.explanation = explanation + + +def interpret(source, frame, should_fail=False): + mod = ast.parse(source) + visitor = DebugInterpreter(frame) + try: + visitor.visit(mod) + except Failure: + failure = sys.exc_info()[1] + return getfailure(failure) + if should_fail: + return ("(assertion failed, but when it was re-run for " + "printing intermediate values, it did not fail. Suggestions: " + "compute assert expression before the assert or use --no-assert)") + +def run(offending_line, frame=None): + if frame is None: + frame = py.code.Frame(sys._getframe(1)) + return interpret(offending_line, frame) + +def getfailure(failure): + explanation = _format_explanation(failure.explanation) + value = failure.cause[1] + if str(value): + lines = explanation.splitlines() + if not lines: + lines.append("") + lines[0] += " << %s" % (value,) + explanation = "\n".join(lines) + text = "%s: %s" % (failure.cause[0].__name__, explanation) + if text.startswith("AssertionError: assert "): + text = text[16:] + return text + + +operator_map = { + ast.BitOr : "|", + ast.BitXor : "^", + ast.BitAnd : "&", + ast.LShift : "<<", + ast.RShift : ">>", + ast.Add : "+", + ast.Sub : "-", + ast.Mult : "*", + ast.Div : "/", + ast.FloorDiv : "//", + ast.Mod : "%", + ast.Eq : "==", + ast.NotEq : "!=", + ast.Lt : "<", + ast.LtE : "<=", + ast.Gt : ">", + ast.GtE : ">=", + ast.Pow : "**", + ast.Is : "is", + ast.IsNot : "is not", + ast.In : "in", + ast.NotIn : "not in" +} + +unary_map = { + ast.Not : "not %s", + ast.Invert : "~%s", + ast.USub : "-%s", + ast.UAdd : "+%s" +} + + +class DebugInterpreter(ast.NodeVisitor): + """Interpret AST nodes to gleam useful debugging information. """ + + def __init__(self, frame): + self.frame = frame + + def generic_visit(self, node): + # Fallback when we don't have a special implementation. + if _is_ast_expr(node): + mod = ast.Expression(node) + co = self._compile(mod) + try: + result = self.frame.eval(co) + except Exception: + raise Failure() + explanation = self.frame.repr(result) + return explanation, result + elif _is_ast_stmt(node): + mod = ast.Module([node]) + co = self._compile(mod, "exec") + try: + self.frame.exec_(co) + except Exception: + raise Failure() + return None, None + else: + raise AssertionError("can't handle %s" %(node,)) + + def _compile(self, source, mode="eval"): + return compile(source, "<assertion interpretation>", mode) + + def visit_Expr(self, expr): + return self.visit(expr.value) + + def visit_Module(self, mod): + for stmt in mod.body: + self.visit(stmt) + + def visit_Name(self, name): + explanation, result = self.generic_visit(name) + # See if the name is local. + source = "%r in locals() is not globals()" % (name.id,) + co = self._compile(source) + try: + local = self.frame.eval(co) + except Exception: + # have to assume it isn't + local = False + if not local: + return name.id, result + return explanation, result + + def visit_Compare(self, comp): + left = comp.left + left_explanation, left_result = self.visit(left) + for op, next_op in zip(comp.ops, comp.comparators): + next_explanation, next_result = self.visit(next_op) + op_symbol = operator_map[op.__class__] + explanation = "%s %s %s" % (left_explanation, op_symbol, + next_explanation) + source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,) + co = self._compile(source) + try: + result = self.frame.eval(co, __exprinfo_left=left_result, + __exprinfo_right=next_result) + except Exception: + raise Failure(explanation) + try: + if not result: + break + except KeyboardInterrupt: + raise + except: + break + left_explanation, left_result = next_explanation, next_result + + rcomp = py.code._reprcompare + if rcomp: + res = rcomp(op_symbol, left_result, next_result) + if res: + explanation = res + return explanation, result + + def visit_BoolOp(self, boolop): + is_or = isinstance(boolop.op, ast.Or) + explanations = [] + for operand in boolop.values: + explanation, result = self.visit(operand) + explanations.append(explanation) + if result == is_or: + break + name = is_or and " or " or " and " + explanation = "(" + name.join(explanations) + ")" + return explanation, result + + def visit_UnaryOp(self, unary): + pattern = unary_map[unary.op.__class__] + operand_explanation, operand_result = self.visit(unary.operand) + explanation = pattern % (operand_explanation,) + co = self._compile(pattern % ("__exprinfo_expr",)) + try: + result = self.frame.eval(co, __exprinfo_expr=operand_result) + except Exception: + raise Failure(explanation) + return explanation, result + + def visit_BinOp(self, binop): + left_explanation, left_result = self.visit(binop.left) + right_explanation, right_result = self.visit(binop.right) + symbol = operator_map[binop.op.__class__] + explanation = "(%s %s %s)" % (left_explanation, symbol, + right_explanation) + source = "__exprinfo_left %s __exprinfo_right" % (symbol,) + co = self._compile(source) + try: + result = self.frame.eval(co, __exprinfo_left=left_result, + __exprinfo_right=right_result) + except Exception: + raise Failure(explanation) + return explanation, result + + def visit_Call(self, call): + func_explanation, func = self.visit(call.func) + arg_explanations = [] + ns = {"__exprinfo_func" : func} + arguments = [] + for arg in call.args: + arg_explanation, arg_result = self.visit(arg) + arg_name = "__exprinfo_%s" % (len(ns),) + ns[arg_name] = arg_result + arguments.append(arg_name) + arg_explanations.append(arg_explanation) + for keyword in call.keywords: + arg_explanation, arg_result = self.visit(keyword.value) + arg_name = "__exprinfo_%s" % (len(ns),) + ns[arg_name] = arg_result + keyword_source = "%s=%%s" % (keyword.arg) + arguments.append(keyword_source % (arg_name,)) + arg_explanations.append(keyword_source % (arg_explanation,)) + if call.starargs: + arg_explanation, arg_result = self.visit(call.starargs) + arg_name = "__exprinfo_star" + ns[arg_name] = arg_result + arguments.append("*%s" % (arg_name,)) + arg_explanations.append("*%s" % (arg_explanation,)) + if call.kwargs: + arg_explanation, arg_result = self.visit(call.kwargs) + arg_name = "__exprinfo_kwds" + ns[arg_name] = arg_result + arguments.append("**%s" % (arg_name,)) + arg_explanations.append("**%s" % (arg_explanation,)) + args_explained = ", ".join(arg_explanations) + explanation = "%s(%s)" % (func_explanation, args_explained) + args = ", ".join(arguments) + source = "__exprinfo_func(%s)" % (args,) + co = self._compile(source) + try: + result = self.frame.eval(co, **ns) + except Exception: + raise Failure(explanation) + pattern = "%s\n{%s = %s\n}" + rep = self.frame.repr(result) + explanation = pattern % (rep, rep, explanation) + return explanation, result + + def _is_builtin_name(self, name): + pattern = "%r not in globals() and %r not in locals()" + source = pattern % (name.id, name.id) + co = self._compile(source) + try: + return self.frame.eval(co) + except Exception: + return False + + def visit_Attribute(self, attr): + if not isinstance(attr.ctx, ast.Load): + return self.generic_visit(attr) + source_explanation, source_result = self.visit(attr.value) + explanation = "%s.%s" % (source_explanation, attr.attr) + source = "__exprinfo_expr.%s" % (attr.attr,) + co = self._compile(source) + try: + result = self.frame.eval(co, __exprinfo_expr=source_result) + except Exception: + raise Failure(explanation) + explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result), + self.frame.repr(result), + source_explanation, attr.attr) + # Check if the attr is from an instance. + source = "%r in getattr(__exprinfo_expr, '__dict__', {})" + source = source % (attr.attr,) + co = self._compile(source) + try: + from_instance = self.frame.eval(co, __exprinfo_expr=source_result) + except Exception: + from_instance = True + if from_instance: + rep = self.frame.repr(result) + pattern = "%s\n{%s = %s\n}" + explanation = pattern % (rep, rep, explanation) + return explanation, result + + def visit_Assert(self, assrt): + test_explanation, test_result = self.visit(assrt.test) + if test_explanation.startswith("False\n{False =") and \ + test_explanation.endswith("\n"): + test_explanation = test_explanation[15:-2] + explanation = "assert %s" % (test_explanation,) + if not test_result: + try: + raise BuiltinAssertionError + except Exception: + raise Failure(explanation) + return explanation, test_result + + def visit_Assign(self, assign): + value_explanation, value_result = self.visit(assign.value) + explanation = "... = %s" % (value_explanation,) + name = ast.Name("__exprinfo_expr", ast.Load(), + lineno=assign.value.lineno, + col_offset=assign.value.col_offset) + new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno, + col_offset=assign.col_offset) + mod = ast.Module([new_assign]) + co = self._compile(mod, "exec") + try: + self.frame.exec_(co, __exprinfo_expr=value_result) + except Exception: + raise Failure(explanation) + return explanation, value_result diff --git a/venv/lib/python3.5/site-packages/py/_code/_assertionold.py b/venv/lib/python3.5/site-packages/py/_code/_assertionold.py new file mode 100644 index 0000000..98786e4 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/_assertionold.py @@ -0,0 +1,555 @@ +import py +import sys, inspect +from compiler import parse, ast, pycodegen +from py._code.assertion import BuiltinAssertionError, _format_explanation + +passthroughex = py.builtin._sysex + +class Failure: + def __init__(self, node): + self.exc, self.value, self.tb = sys.exc_info() + self.node = node + +class View(object): + """View base class. + + If C is a subclass of View, then C(x) creates a proxy object around + the object x. The actual class of the proxy is not C in general, + but a *subclass* of C determined by the rules below. To avoid confusion + we call view class the class of the proxy (a subclass of C, so of View) + and object class the class of x. + + Attributes and methods not found in the proxy are automatically read on x. + Other operations like setting attributes are performed on the proxy, as + determined by its view class. The object x is available from the proxy + as its __obj__ attribute. + + The view class selection is determined by the __view__ tuples and the + optional __viewkey__ method. By default, the selected view class is the + most specific subclass of C whose __view__ mentions the class of x. + If no such subclass is found, the search proceeds with the parent + object classes. For example, C(True) will first look for a subclass + of C with __view__ = (..., bool, ...) and only if it doesn't find any + look for one with __view__ = (..., int, ...), and then ..., object,... + If everything fails the class C itself is considered to be the default. + + Alternatively, the view class selection can be driven by another aspect + of the object x, instead of the class of x, by overriding __viewkey__. + See last example at the end of this module. + """ + + _viewcache = {} + __view__ = () + + def __new__(rootclass, obj, *args, **kwds): + self = object.__new__(rootclass) + self.__obj__ = obj + self.__rootclass__ = rootclass + key = self.__viewkey__() + try: + self.__class__ = self._viewcache[key] + except KeyError: + self.__class__ = self._selectsubclass(key) + return self + + def __getattr__(self, attr): + # attributes not found in the normal hierarchy rooted on View + # are looked up in the object's real class + return getattr(self.__obj__, attr) + + def __viewkey__(self): + return self.__obj__.__class__ + + def __matchkey__(self, key, subclasses): + if inspect.isclass(key): + keys = inspect.getmro(key) + else: + keys = [key] + for key in keys: + result = [C for C in subclasses if key in C.__view__] + if result: + return result + return [] + + def _selectsubclass(self, key): + subclasses = list(enumsubclasses(self.__rootclass__)) + for C in subclasses: + if not isinstance(C.__view__, tuple): + C.__view__ = (C.__view__,) + choices = self.__matchkey__(key, subclasses) + if not choices: + return self.__rootclass__ + elif len(choices) == 1: + return choices[0] + else: + # combine the multiple choices + return type('?', tuple(choices), {}) + + def __repr__(self): + return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__) + + +def enumsubclasses(cls): + for subcls in cls.__subclasses__(): + for subsubclass in enumsubclasses(subcls): + yield subsubclass + yield cls + + +class Interpretable(View): + """A parse tree node with a few extra methods.""" + explanation = None + + def is_builtin(self, frame): + return False + + def eval(self, frame): + # fall-back for unknown expression nodes + try: + expr = ast.Expression(self.__obj__) + expr.filename = '<eval>' + self.__obj__.filename = '<eval>' + co = pycodegen.ExpressionCodeGenerator(expr).getCode() + result = frame.eval(co) + except passthroughex: + raise + except: + raise Failure(self) + self.result = result + self.explanation = self.explanation or frame.repr(self.result) + + def run(self, frame): + # fall-back for unknown statement nodes + try: + expr = ast.Module(None, ast.Stmt([self.__obj__])) + expr.filename = '<run>' + co = pycodegen.ModuleCodeGenerator(expr).getCode() + frame.exec_(co) + except passthroughex: + raise + except: + raise Failure(self) + + def nice_explanation(self): + return _format_explanation(self.explanation) + + +class Name(Interpretable): + __view__ = ast.Name + + def is_local(self, frame): + source = '%r in locals() is not globals()' % self.name + try: + return frame.is_true(frame.eval(source)) + except passthroughex: + raise + except: + return False + + def is_global(self, frame): + source = '%r in globals()' % self.name + try: + return frame.is_true(frame.eval(source)) + except passthroughex: + raise + except: + return False + + def is_builtin(self, frame): + source = '%r not in locals() and %r not in globals()' % ( + self.name, self.name) + try: + return frame.is_true(frame.eval(source)) + except passthroughex: + raise + except: + return False + + def eval(self, frame): + super(Name, self).eval(frame) + if not self.is_local(frame): + self.explanation = self.name + +class Compare(Interpretable): + __view__ = ast.Compare + + def eval(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + for operation, expr2 in self.ops: + if hasattr(self, 'result'): + # shortcutting in chained expressions + if not frame.is_true(self.result): + break + expr2 = Interpretable(expr2) + expr2.eval(frame) + self.explanation = "%s %s %s" % ( + expr.explanation, operation, expr2.explanation) + source = "__exprinfo_left %s __exprinfo_right" % operation + try: + self.result = frame.eval(source, + __exprinfo_left=expr.result, + __exprinfo_right=expr2.result) + except passthroughex: + raise + except: + raise Failure(self) + expr = expr2 + +class And(Interpretable): + __view__ = ast.And + + def eval(self, frame): + explanations = [] + for expr in self.nodes: + expr = Interpretable(expr) + expr.eval(frame) + explanations.append(expr.explanation) + self.result = expr.result + if not frame.is_true(expr.result): + break + self.explanation = '(' + ' and '.join(explanations) + ')' + +class Or(Interpretable): + __view__ = ast.Or + + def eval(self, frame): + explanations = [] + for expr in self.nodes: + expr = Interpretable(expr) + expr.eval(frame) + explanations.append(expr.explanation) + self.result = expr.result + if frame.is_true(expr.result): + break + self.explanation = '(' + ' or '.join(explanations) + ')' + + +# == Unary operations == +keepalive = [] +for astclass, astpattern in { + ast.Not : 'not __exprinfo_expr', + ast.Invert : '(~__exprinfo_expr)', + }.items(): + + class UnaryArith(Interpretable): + __view__ = astclass + + def eval(self, frame, astpattern=astpattern): + expr = Interpretable(self.expr) + expr.eval(frame) + self.explanation = astpattern.replace('__exprinfo_expr', + expr.explanation) + try: + self.result = frame.eval(astpattern, + __exprinfo_expr=expr.result) + except passthroughex: + raise + except: + raise Failure(self) + + keepalive.append(UnaryArith) + +# == Binary operations == +for astclass, astpattern in { + ast.Add : '(__exprinfo_left + __exprinfo_right)', + ast.Sub : '(__exprinfo_left - __exprinfo_right)', + ast.Mul : '(__exprinfo_left * __exprinfo_right)', + ast.Div : '(__exprinfo_left / __exprinfo_right)', + ast.Mod : '(__exprinfo_left % __exprinfo_right)', + ast.Power : '(__exprinfo_left ** __exprinfo_right)', + }.items(): + + class BinaryArith(Interpretable): + __view__ = astclass + + def eval(self, frame, astpattern=astpattern): + left = Interpretable(self.left) + left.eval(frame) + right = Interpretable(self.right) + right.eval(frame) + self.explanation = (astpattern + .replace('__exprinfo_left', left .explanation) + .replace('__exprinfo_right', right.explanation)) + try: + self.result = frame.eval(astpattern, + __exprinfo_left=left.result, + __exprinfo_right=right.result) + except passthroughex: + raise + except: + raise Failure(self) + + keepalive.append(BinaryArith) + + +class CallFunc(Interpretable): + __view__ = ast.CallFunc + + def is_bool(self, frame): + source = 'isinstance(__exprinfo_value, bool)' + try: + return frame.is_true(frame.eval(source, + __exprinfo_value=self.result)) + except passthroughex: + raise + except: + return False + + def eval(self, frame): + node = Interpretable(self.node) + node.eval(frame) + explanations = [] + vars = {'__exprinfo_fn': node.result} + source = '__exprinfo_fn(' + for a in self.args: + if isinstance(a, ast.Keyword): + keyword = a.name + a = a.expr + else: + keyword = None + a = Interpretable(a) + a.eval(frame) + argname = '__exprinfo_%d' % len(vars) + vars[argname] = a.result + if keyword is None: + source += argname + ',' + explanations.append(a.explanation) + else: + source += '%s=%s,' % (keyword, argname) + explanations.append('%s=%s' % (keyword, a.explanation)) + if self.star_args: + star_args = Interpretable(self.star_args) + star_args.eval(frame) + argname = '__exprinfo_star' + vars[argname] = star_args.result + source += '*' + argname + ',' + explanations.append('*' + star_args.explanation) + if self.dstar_args: + dstar_args = Interpretable(self.dstar_args) + dstar_args.eval(frame) + argname = '__exprinfo_kwds' + vars[argname] = dstar_args.result + source += '**' + argname + ',' + explanations.append('**' + dstar_args.explanation) + self.explanation = "%s(%s)" % ( + node.explanation, ', '.join(explanations)) + if source.endswith(','): + source = source[:-1] + source += ')' + try: + self.result = frame.eval(source, **vars) + except passthroughex: + raise + except: + raise Failure(self) + if not node.is_builtin(frame) or not self.is_bool(frame): + r = frame.repr(self.result) + self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) + +class Getattr(Interpretable): + __view__ = ast.Getattr + + def eval(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + source = '__exprinfo_expr.%s' % self.attrname + try: + self.result = frame.eval(source, __exprinfo_expr=expr.result) + except passthroughex: + raise + except: + raise Failure(self) + self.explanation = '%s.%s' % (expr.explanation, self.attrname) + # if the attribute comes from the instance, its value is interesting + source = ('hasattr(__exprinfo_expr, "__dict__") and ' + '%r in __exprinfo_expr.__dict__' % self.attrname) + try: + from_instance = frame.is_true( + frame.eval(source, __exprinfo_expr=expr.result)) + except passthroughex: + raise + except: + from_instance = True + if from_instance: + r = frame.repr(self.result) + self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) + +# == Re-interpretation of full statements == + +class Assert(Interpretable): + __view__ = ast.Assert + + def run(self, frame): + test = Interpretable(self.test) + test.eval(frame) + # simplify 'assert False where False = ...' + if (test.explanation.startswith('False\n{False = ') and + test.explanation.endswith('\n}')): + test.explanation = test.explanation[15:-2] + # print the result as 'assert <explanation>' + self.result = test.result + self.explanation = 'assert ' + test.explanation + if not frame.is_true(test.result): + try: + raise BuiltinAssertionError + except passthroughex: + raise + except: + raise Failure(self) + +class Assign(Interpretable): + __view__ = ast.Assign + + def run(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + self.result = expr.result + self.explanation = '... = ' + expr.explanation + # fall-back-run the rest of the assignment + ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr')) + mod = ast.Module(None, ast.Stmt([ass])) + mod.filename = '<run>' + co = pycodegen.ModuleCodeGenerator(mod).getCode() + try: + frame.exec_(co, __exprinfo_expr=expr.result) + except passthroughex: + raise + except: + raise Failure(self) + +class Discard(Interpretable): + __view__ = ast.Discard + + def run(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + self.result = expr.result + self.explanation = expr.explanation + +class Stmt(Interpretable): + __view__ = ast.Stmt + + def run(self, frame): + for stmt in self.nodes: + stmt = Interpretable(stmt) + stmt.run(frame) + + +def report_failure(e): + explanation = e.node.nice_explanation() + if explanation: + explanation = ", in: " + explanation + else: + explanation = "" + sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation)) + +def check(s, frame=None): + if frame is None: + frame = sys._getframe(1) + frame = py.code.Frame(frame) + expr = parse(s, 'eval') + assert isinstance(expr, ast.Expression) + node = Interpretable(expr.node) + try: + node.eval(frame) + except passthroughex: + raise + except Failure: + e = sys.exc_info()[1] + report_failure(e) + else: + if not frame.is_true(node.result): + sys.stderr.write("assertion failed: %s\n" % node.nice_explanation()) + + +########################################################### +# API / Entry points +# ######################################################### + +def interpret(source, frame, should_fail=False): + module = Interpretable(parse(source, 'exec').node) + #print "got module", module + if isinstance(frame, py.std.types.FrameType): + frame = py.code.Frame(frame) + try: + module.run(frame) + except Failure: + e = sys.exc_info()[1] + return getfailure(e) + except passthroughex: + raise + except: + import traceback + traceback.print_exc() + if should_fail: + return ("(assertion failed, but when it was re-run for " + "printing intermediate values, it did not fail. Suggestions: " + "compute assert expression before the assert or use --nomagic)") + else: + return None + +def getmsg(excinfo): + if isinstance(excinfo, tuple): + excinfo = py.code.ExceptionInfo(excinfo) + #frame, line = gettbline(tb) + #frame = py.code.Frame(frame) + #return interpret(line, frame) + + tb = excinfo.traceback[-1] + source = str(tb.statement).strip() + x = interpret(source, tb.frame, should_fail=True) + if not isinstance(x, str): + raise TypeError("interpret returned non-string %r" % (x,)) + return x + +def getfailure(e): + explanation = e.node.nice_explanation() + if str(e.value): + lines = explanation.split('\n') + lines[0] += " << %s" % (e.value,) + explanation = '\n'.join(lines) + text = "%s: %s" % (e.exc.__name__, explanation) + if text.startswith('AssertionError: assert '): + text = text[16:] + return text + +def run(s, frame=None): + if frame is None: + frame = sys._getframe(1) + frame = py.code.Frame(frame) + module = Interpretable(parse(s, 'exec').node) + try: + module.run(frame) + except Failure: + e = sys.exc_info()[1] + report_failure(e) + + +if __name__ == '__main__': + # example: + def f(): + return 5 + def g(): + return 3 + def h(x): + return 'never' + check("f() * g() == 5") + check("not f()") + check("not (f() and g() or 0)") + check("f() == g()") + i = 4 + check("i == f()") + check("len(f()) == 0") + check("isinstance(2+3+4, float)") + + run("x = i") + check("x == 5") + + run("assert not f(), 'oops'") + run("a, b, c = 1, 2") + run("a, b, c = f()") + + check("max([f(),g()]) == 4") + check("'hello'[g()] == 'h'") + run("'guk%d' % h(f())") diff --git a/venv/lib/python3.5/site-packages/py/_code/_py2traceback.py b/venv/lib/python3.5/site-packages/py/_code/_py2traceback.py new file mode 100644 index 0000000..227cb96 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/_py2traceback.py @@ -0,0 +1,79 @@ +# copied from python-2.7.3's traceback.py +# CHANGES: +# - some_str is replaced, trying to create unicode strings +# +import types + +def format_exception_only(etype, value): + """Format the exception part of a traceback. + + The arguments are the exception type and value such as given by + sys.last_type and sys.last_value. The return value is a list of + strings, each ending in a newline. + + Normally, the list contains a single string; however, for + SyntaxError exceptions, it contains several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the list. + + """ + + # An instance should not have a meaningful value parameter, but + # sometimes does, particularly for string exceptions, such as + # >>> raise string1, string2 # deprecated + # + # Clear these out first because issubtype(string1, SyntaxError) + # would throw another exception and mask the original problem. + if (isinstance(etype, BaseException) or + isinstance(etype, types.InstanceType) or + etype is None or type(etype) is str): + return [_format_final_exc_line(etype, value)] + + stype = etype.__name__ + + if not issubclass(etype, SyntaxError): + return [_format_final_exc_line(stype, value)] + + # It was a syntax error; show exactly where the problem was found. + lines = [] + try: + msg, (filename, lineno, offset, badline) = value.args + except Exception: + pass + else: + filename = filename or "<string>" + lines.append(' File "%s", line %d\n' % (filename, lineno)) + if badline is not None: + lines.append(' %s\n' % badline.strip()) + if offset is not None: + caretspace = badline.rstrip('\n')[:offset].lstrip() + # non-space whitespace (likes tabs) must be kept for alignment + caretspace = ((c.isspace() and c or ' ') for c in caretspace) + # only three spaces to account for offset1 == pos 0 + lines.append(' %s^\n' % ''.join(caretspace)) + value = msg + + lines.append(_format_final_exc_line(stype, value)) + return lines + +def _format_final_exc_line(etype, value): + """Return a list of a single line -- normal case for format_exception_only""" + valuestr = _some_str(value) + if value is None or not valuestr: + line = "%s\n" % etype + else: + line = "%s: %s\n" % (etype, valuestr) + return line + +def _some_str(value): + try: + return unicode(value) + except Exception: + try: + return str(value) + except Exception: + pass + return '<unprintable %s object>' % type(value).__name__ diff --git a/venv/lib/python3.5/site-packages/py/_code/assertion.py b/venv/lib/python3.5/site-packages/py/_code/assertion.py new file mode 100644 index 0000000..3bcb8cd --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/assertion.py @@ -0,0 +1,94 @@ +import sys +import py + +BuiltinAssertionError = py.builtin.builtins.AssertionError + +_reprcompare = None # if set, will be called by assert reinterp for comparison ops + +def _format_explanation(explanation): + """This formats an explanation + + Normally all embedded newlines are escaped, however there are + three exceptions: \n{, \n} and \n~. The first two are intended + cover nested explanations, see function and attribute explanations + for examples (.visit_Call(), visit_Attribute()). The last one is + for when one explanation needs to span multiple lines, e.g. when + displaying diffs. + """ + raw_lines = (explanation or '').split('\n') + # escape newlines not followed by {, } and ~ + lines = [raw_lines[0]] + for l in raw_lines[1:]: + if l.startswith('{') or l.startswith('}') or l.startswith('~'): + lines.append(l) + else: + lines[-1] += '\\n' + l + + result = lines[:1] + stack = [0] + stackcnt = [0] + for line in lines[1:]: + if line.startswith('{'): + if stackcnt[-1]: + s = 'and ' + else: + s = 'where ' + stack.append(len(result)) + stackcnt[-1] += 1 + stackcnt.append(0) + result.append(' +' + ' '*(len(stack)-1) + s + line[1:]) + elif line.startswith('}'): + assert line.startswith('}') + stack.pop() + stackcnt.pop() + result[stack[-1]] += line[1:] + else: + assert line.startswith('~') + result.append(' '*len(stack) + line[1:]) + assert len(stack) == 1 + return '\n'.join(result) + + +class AssertionError(BuiltinAssertionError): + def __init__(self, *args): + BuiltinAssertionError.__init__(self, *args) + if args: + try: + self.msg = str(args[0]) + except py.builtin._sysex: + raise + except: + self.msg = "<[broken __repr__] %s at %0xd>" %( + args[0].__class__, id(args[0])) + else: + f = py.code.Frame(sys._getframe(1)) + try: + source = f.code.fullsource + if source is not None: + try: + source = source.getstatement(f.lineno, assertion=True) + except IndexError: + source = None + else: + source = str(source.deindent()).strip() + except py.error.ENOENT: + source = None + # this can also occur during reinterpretation, when the + # co_filename is set to "<run>". + if source: + self.msg = reinterpret(source, f, should_fail=True) + else: + self.msg = "<could not determine information>" + if not self.args: + self.args = (self.msg,) + +if sys.version_info > (3, 0): + AssertionError.__module__ = "builtins" + reinterpret_old = "old reinterpretation not available for py3" +else: + from py._code._assertionold import interpret as reinterpret_old +if sys.version_info >= (2, 6) or (sys.platform.startswith("java")): + from py._code._assertionnew import interpret as reinterpret +else: + reinterpret = reinterpret_old + diff --git a/venv/lib/python3.5/site-packages/py/_code/code.py b/venv/lib/python3.5/site-packages/py/_code/code.py new file mode 100644 index 0000000..822cf4b --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/code.py @@ -0,0 +1,787 @@ +import py +import sys +from inspect import CO_VARARGS, CO_VARKEYWORDS + +builtin_repr = repr + +reprlib = py.builtin._tryimport('repr', 'reprlib') + +if sys.version_info[0] >= 3: + from traceback import format_exception_only +else: + from py._code._py2traceback import format_exception_only + +class Code(object): + """ wrapper around Python code objects """ + def __init__(self, rawcode): + if not hasattr(rawcode, "co_filename"): + rawcode = py.code.getrawcode(rawcode) + try: + self.filename = rawcode.co_filename + self.firstlineno = rawcode.co_firstlineno - 1 + self.name = rawcode.co_name + except AttributeError: + raise TypeError("not a code object: %r" %(rawcode,)) + self.raw = rawcode + + def __eq__(self, other): + return self.raw == other.raw + + def __ne__(self, other): + return not self == other + + @property + def path(self): + """ return a path object pointing to source code (note that it + might not point to an actually existing file). """ + p = py.path.local(self.raw.co_filename) + # maybe don't try this checking + if not p.check(): + # XXX maybe try harder like the weird logic + # in the standard lib [linecache.updatecache] does? + p = self.raw.co_filename + return p + + @property + def fullsource(self): + """ return a py.code.Source object for the full source file of the code + """ + from py._code import source + full, _ = source.findsource(self.raw) + return full + + def source(self): + """ return a py.code.Source object for the code object's source only + """ + # return source only for that part of code + return py.code.Source(self.raw) + + def getargs(self, var=False): + """ return a tuple with the argument names for the code object + + if 'var' is set True also return the names of the variable and + keyword arguments when present + """ + # handfull shortcut for getting args + raw = self.raw + argcount = raw.co_argcount + if var: + argcount += raw.co_flags & CO_VARARGS + argcount += raw.co_flags & CO_VARKEYWORDS + return raw.co_varnames[:argcount] + +class Frame(object): + """Wrapper around a Python frame holding f_locals and f_globals + in which expressions can be evaluated.""" + + def __init__(self, frame): + self.lineno = frame.f_lineno - 1 + self.f_globals = frame.f_globals + self.f_locals = frame.f_locals + self.raw = frame + self.code = py.code.Code(frame.f_code) + + @property + def statement(self): + """ statement this frame is at """ + if self.code.fullsource is None: + return py.code.Source("") + return self.code.fullsource.getstatement(self.lineno) + + def eval(self, code, **vars): + """ evaluate 'code' in the frame + + 'vars' are optional additional local variables + + returns the result of the evaluation + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + return eval(code, self.f_globals, f_locals) + + def exec_(self, code, **vars): + """ exec 'code' in the frame + + 'vars' are optiona; additional local variables + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + py.builtin.exec_(code, self.f_globals, f_locals ) + + def repr(self, object): + """ return a 'safe' (non-recursive, one-line) string repr for 'object' + """ + return py.io.saferepr(object) + + def is_true(self, object): + return object + + def getargs(self, var=False): + """ return a list of tuples (name, value) for all arguments + + if 'var' is set True also include the variable and keyword + arguments when present + """ + retval = [] + for arg in self.code.getargs(var): + try: + retval.append((arg, self.f_locals[arg])) + except KeyError: + pass # this can occur when using Psyco + return retval + +class TracebackEntry(object): + """ a single entry in a traceback """ + + _repr_style = None + exprinfo = None + + def __init__(self, rawentry): + self._rawentry = rawentry + self.lineno = rawentry.tb_lineno - 1 + + def set_repr_style(self, mode): + assert mode in ("short", "long") + self._repr_style = mode + + @property + def frame(self): + return py.code.Frame(self._rawentry.tb_frame) + + @property + def relline(self): + return self.lineno - self.frame.code.firstlineno + + def __repr__(self): + return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1) + + @property + def statement(self): + """ py.code.Source object for the current statement """ + source = self.frame.code.fullsource + return source.getstatement(self.lineno) + + @property + def path(self): + """ path to the source code """ + return self.frame.code.path + + def getlocals(self): + return self.frame.f_locals + locals = property(getlocals, None, None, "locals of underlaying frame") + + def reinterpret(self): + """Reinterpret the failing statement and returns a detailed information + about what operations are performed.""" + if self.exprinfo is None: + source = str(self.statement).strip() + x = py.code._reinterpret(source, self.frame, should_fail=True) + if not isinstance(x, str): + raise TypeError("interpret returned non-string %r" % (x,)) + self.exprinfo = x + return self.exprinfo + + def getfirstlinesource(self): + # on Jython this firstlineno can be -1 apparently + return max(self.frame.code.firstlineno, 0) + + def getsource(self, astcache=None): + """ return failing source code. """ + # we use the passed in astcache to not reparse asttrees + # within exception info printing + from py._code.source import getstatementrange_ast + source = self.frame.code.fullsource + if source is None: + return None + key = astnode = None + if astcache is not None: + key = self.frame.code.path + if key is not None: + astnode = astcache.get(key, None) + start = self.getfirstlinesource() + try: + astnode, _, end = getstatementrange_ast(self.lineno, source, + astnode=astnode) + except SyntaxError: + end = self.lineno + 1 + else: + if key is not None: + astcache[key] = astnode + return source[start:end] + + source = property(getsource) + + def ishidden(self): + """ return True if the current frame has a var __tracebackhide__ + resolving to True + + mostly for internal use + """ + try: + return self.frame.f_locals['__tracebackhide__'] + except KeyError: + try: + return self.frame.f_globals['__tracebackhide__'] + except KeyError: + return False + + def __str__(self): + try: + fn = str(self.path) + except py.error.Error: + fn = '???' + name = self.frame.code.name + try: + line = str(self.statement).lstrip() + except KeyboardInterrupt: + raise + except: + line = "???" + return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) + + def name(self): + return self.frame.code.raw.co_name + name = property(name, None, None, "co_name of underlaying code") + +class Traceback(list): + """ Traceback objects encapsulate and offer higher level + access to Traceback entries. + """ + Entry = TracebackEntry + def __init__(self, tb): + """ initialize from given python traceback object. """ + if hasattr(tb, 'tb_next'): + def f(cur): + while cur is not None: + yield self.Entry(cur) + cur = cur.tb_next + list.__init__(self, f(tb)) + else: + list.__init__(self, tb) + + def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None): + """ return a Traceback instance wrapping part of this Traceback + + by provding any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined + + this allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback) + """ + for x in self: + code = x.frame.code + codepath = code.path + if ((path is None or codepath == path) and + (excludepath is None or not hasattr(codepath, 'relto') or + not codepath.relto(excludepath)) and + (lineno is None or x.lineno == lineno) and + (firstlineno is None or x.frame.code.firstlineno == firstlineno)): + return Traceback(x._rawentry) + return self + + def __getitem__(self, key): + val = super(Traceback, self).__getitem__(key) + if isinstance(key, type(slice(0))): + val = self.__class__(val) + return val + + def filter(self, fn=lambda x: not x.ishidden()): + """ return a Traceback instance with certain items removed + + fn is a function that gets a single argument, a TracebackItem + instance, and should return True when the item should be added + to the Traceback, False when not + + by default this removes all the TracebackItems which are hidden + (see ishidden() above) + """ + return Traceback(filter(fn, self)) + + def getcrashentry(self): + """ return last non-hidden traceback entry that lead + to the exception of a traceback. + """ + for i in range(-1, -len(self)-1, -1): + entry = self[i] + if not entry.ishidden(): + return entry + return self[-1] + + def recursionindex(self): + """ return the index of the frame/TracebackItem where recursion + originates if appropriate, None if no recursion occurred + """ + cache = {} + for i, entry in enumerate(self): + # id for the code.raw is needed to work around + # the strange metaprogramming in the decorator lib from pypi + # which generates code objects that have hash/value equality + #XXX needs a test + key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno + #print "checking for recursion at", key + l = cache.setdefault(key, []) + if l: + f = entry.frame + loc = f.f_locals + for otherloc in l: + if f.is_true(f.eval(co_equal, + __recursioncache_locals_1=loc, + __recursioncache_locals_2=otherloc)): + return i + l.append(entry.frame.f_locals) + return None + +co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', + '?', 'eval') + +class ExceptionInfo(object): + """ wraps sys.exc_info() objects and offers + help for navigating the traceback. + """ + _striptext = '' + def __init__(self, tup=None, exprinfo=None): + if tup is None: + tup = sys.exc_info() + if exprinfo is None and isinstance(tup[1], AssertionError): + exprinfo = getattr(tup[1], 'msg', None) + if exprinfo is None: + exprinfo = str(tup[1]) + if exprinfo and exprinfo.startswith('assert '): + self._striptext = 'AssertionError: ' + self._excinfo = tup + #: the exception class + self.type = tup[0] + #: the exception instance + self.value = tup[1] + #: the exception raw traceback + self.tb = tup[2] + #: the exception type name + self.typename = self.type.__name__ + #: the exception traceback (py.code.Traceback instance) + self.traceback = py.code.Traceback(self.tb) + + def __repr__(self): + return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback)) + + def exconly(self, tryshort=False): + """ return the exception as a string + + when 'tryshort' resolves to True, and the exception is a + py.code._AssertionError, only the actual exception part of + the exception representation is returned (so 'AssertionError: ' is + removed from the beginning) + """ + lines = format_exception_only(self.type, self.value) + text = ''.join(lines) + text = text.rstrip() + if tryshort: + if text.startswith(self._striptext): + text = text[len(self._striptext):] + return text + + def errisinstance(self, exc): + """ return True if the exception is an instance of exc """ + return isinstance(self.value, exc) + + def _getreprcrash(self): + exconly = self.exconly(tryshort=True) + entry = self.traceback.getcrashentry() + path, lineno = entry.frame.code.raw.co_filename, entry.lineno + return ReprFileLocation(path, lineno+1, exconly) + + def getrepr(self, showlocals=False, style="long", + abspath=False, tbfilter=True, funcargs=False): + """ return str()able representation of this exception info. + showlocals: show locals per traceback entry + style: long|short|no|native traceback style + tbfilter: hide entries (where __tracebackhide__ is true) + + in case of style==native, tbfilter and showlocals is ignored. + """ + if style == 'native': + return ReprExceptionInfo(ReprTracebackNative( + py.std.traceback.format_exception( + self.type, + self.value, + self.traceback[0]._rawentry, + )), self._getreprcrash()) + + fmt = FormattedExcinfo(showlocals=showlocals, style=style, + abspath=abspath, tbfilter=tbfilter, funcargs=funcargs) + return fmt.repr_excinfo(self) + + def __str__(self): + entry = self.traceback[-1] + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return str(loc) + + def __unicode__(self): + entry = self.traceback[-1] + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return loc.__unicode__() + + +class FormattedExcinfo(object): + """ presenting information about failing Functions and Generators. """ + # for traceback entries + flow_marker = ">" + fail_marker = "E" + + def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False): + self.showlocals = showlocals + self.style = style + self.tbfilter = tbfilter + self.funcargs = funcargs + self.abspath = abspath + self.astcache = {} + + def _getindent(self, source): + # figure out indent for given source + try: + s = str(source.getstatement(len(source)-1)) + except KeyboardInterrupt: + raise + except: + try: + s = str(source[-1]) + except KeyboardInterrupt: + raise + except: + return 0 + return 4 + (len(s) - len(s.lstrip())) + + def _getentrysource(self, entry): + source = entry.getsource(self.astcache) + if source is not None: + source = source.deindent() + return source + + def _saferepr(self, obj): + return py.io.saferepr(obj) + + def repr_args(self, entry): + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(var=True): + args.append((argname, self._saferepr(argvalue))) + return ReprFuncArgs(args) + + def get_source(self, source, line_index=-1, excinfo=None, short=False): + """ return formatted and marked up source lines. """ + lines = [] + if source is None or line_index >= len(source.lines): + source = py.code.Source("???") + line_index = 0 + if line_index < 0: + line_index += len(source) + space_prefix = " " + if short: + lines.append(space_prefix + source.lines[line_index].strip()) + else: + for line in source.lines[:line_index]: + lines.append(space_prefix + line) + lines.append(self.flow_marker + " " + source.lines[line_index]) + for line in source.lines[line_index+1:]: + lines.append(space_prefix + line) + if excinfo is not None: + indent = 4 if short else self._getindent(source) + lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) + return lines + + def get_exconly(self, excinfo, indent=4, markall=False): + lines = [] + indent = " " * indent + # get the real exception information out + exlines = excinfo.exconly(tryshort=True).split('\n') + failindent = self.fail_marker + indent[1:] + for line in exlines: + lines.append(failindent + line) + if not markall: + failindent = indent + return lines + + def repr_locals(self, locals): + if self.showlocals: + lines = [] + keys = [loc for loc in locals if loc[0] != "@"] + keys.sort() + for name in keys: + value = locals[name] + if name == '__builtins__': + lines.append("__builtins__ = <builtins>") + else: + # This formatting could all be handled by the + # _repr() function, which is only reprlib.Repr in + # disguise, so is very configurable. + str_repr = self._saferepr(value) + #if len(str_repr) < 70 or not isinstance(value, + # (list, tuple, dict)): + lines.append("%-10s = %s" %(name, str_repr)) + #else: + # self._line("%-10s =\\" % (name,)) + # # XXX + # py.std.pprint.pprint(value, stream=self.excinfowriter) + return ReprLocals(lines) + + def repr_traceback_entry(self, entry, excinfo=None): + source = self._getentrysource(entry) + if source is None: + source = py.code.Source("???") + line_index = 0 + else: + # entry.getfirstlinesource() can be -1, should be 0 on jython + line_index = entry.lineno - max(entry.getfirstlinesource(), 0) + + lines = [] + style = entry._repr_style + if style is None: + style = self.style + if style in ("short", "long"): + short = style == "short" + reprargs = self.repr_args(entry) if not short else None + s = self.get_source(source, line_index, excinfo, short=short) + lines.extend(s) + if short: + message = "in %s" %(entry.name) + else: + message = excinfo and excinfo.typename or "" + path = self._makepath(entry.path) + filelocrepr = ReprFileLocation(path, entry.lineno+1, message) + localsrepr = None + if not short: + localsrepr = self.repr_locals(entry.locals) + return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style) + if excinfo: + lines.extend(self.get_exconly(excinfo, indent=4)) + return ReprEntry(lines, None, None, None, style) + + def _makepath(self, path): + if not self.abspath: + try: + np = py.path.local().bestrelpath(path) + except OSError: + return path + if len(np) < len(str(path)): + path = np + return path + + def repr_traceback(self, excinfo): + traceback = excinfo.traceback + if self.tbfilter: + traceback = traceback.filter() + recursionindex = None + if excinfo.errisinstance(RuntimeError): + if "maximum recursion depth exceeded" in str(excinfo.value): + recursionindex = traceback.recursionindex() + last = traceback[-1] + entries = [] + extraline = None + for index, entry in enumerate(traceback): + einfo = (last == entry) and excinfo or None + reprentry = self.repr_traceback_entry(entry, einfo) + entries.append(reprentry) + if index == recursionindex: + extraline = "!!! Recursion detected (same locals & position)" + break + return ReprTraceback(entries, extraline, style=self.style) + + def repr_excinfo(self, excinfo): + reprtraceback = self.repr_traceback(excinfo) + reprcrash = excinfo._getreprcrash() + return ReprExceptionInfo(reprtraceback, reprcrash) + +class TerminalRepr: + def __str__(self): + s = self.__unicode__() + if sys.version_info[0] < 3: + s = s.encode('utf-8') + return s + + def __unicode__(self): + # FYI this is called from pytest-xdist's serialization of exception + # information. + io = py.io.TextIO() + tw = py.io.TerminalWriter(file=io) + self.toterminal(tw) + return io.getvalue().strip() + + def __repr__(self): + return "<%s instance at %0x>" %(self.__class__, id(self)) + + +class ReprExceptionInfo(TerminalRepr): + def __init__(self, reprtraceback, reprcrash): + self.reprtraceback = reprtraceback + self.reprcrash = reprcrash + self.sections = [] + + def addsection(self, name, content, sep="-"): + self.sections.append((name, content, sep)) + + def toterminal(self, tw): + self.reprtraceback.toterminal(tw) + for name, content, sep in self.sections: + tw.sep(sep, name) + tw.line(content) + +class ReprTraceback(TerminalRepr): + entrysep = "_ " + + def __init__(self, reprentries, extraline, style): + self.reprentries = reprentries + self.extraline = extraline + self.style = style + + def toterminal(self, tw): + # the entries might have different styles + last_style = None + for i, entry in enumerate(self.reprentries): + if entry.style == "long": + tw.line("") + entry.toterminal(tw) + if i < len(self.reprentries) - 1: + next_entry = self.reprentries[i+1] + if entry.style == "long" or \ + entry.style == "short" and next_entry.style == "long": + tw.sep(self.entrysep) + + if self.extraline: + tw.line(self.extraline) + +class ReprTracebackNative(ReprTraceback): + def __init__(self, tblines): + self.style = "native" + self.reprentries = [ReprEntryNative(tblines)] + self.extraline = None + +class ReprEntryNative(TerminalRepr): + style = "native" + + def __init__(self, tblines): + self.lines = tblines + + def toterminal(self, tw): + tw.write("".join(self.lines)) + +class ReprEntry(TerminalRepr): + localssep = "_ " + + def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style): + self.lines = lines + self.reprfuncargs = reprfuncargs + self.reprlocals = reprlocals + self.reprfileloc = filelocrepr + self.style = style + + def toterminal(self, tw): + if self.style == "short": + self.reprfileloc.toterminal(tw) + for line in self.lines: + red = line.startswith("E ") + tw.line(line, bold=True, red=red) + #tw.line("") + return + if self.reprfuncargs: + self.reprfuncargs.toterminal(tw) + for line in self.lines: + red = line.startswith("E ") + tw.line(line, bold=True, red=red) + if self.reprlocals: + #tw.sep(self.localssep, "Locals") + tw.line("") + self.reprlocals.toterminal(tw) + if self.reprfileloc: + if self.lines: + tw.line("") + self.reprfileloc.toterminal(tw) + + def __str__(self): + return "%s\n%s\n%s" % ("\n".join(self.lines), + self.reprlocals, + self.reprfileloc) + +class ReprFileLocation(TerminalRepr): + def __init__(self, path, lineno, message): + self.path = str(path) + self.lineno = lineno + self.message = message + + def toterminal(self, tw): + # filename and lineno output for each entry, + # using an output format that most editors unterstand + msg = self.message + i = msg.find("\n") + if i != -1: + msg = msg[:i] + tw.line("%s:%s: %s" %(self.path, self.lineno, msg)) + +class ReprLocals(TerminalRepr): + def __init__(self, lines): + self.lines = lines + + def toterminal(self, tw): + for line in self.lines: + tw.line(line) + +class ReprFuncArgs(TerminalRepr): + def __init__(self, args): + self.args = args + + def toterminal(self, tw): + if self.args: + linesofar = "" + for name, value in self.args: + ns = "%s = %s" %(name, value) + if len(ns) + len(linesofar) + 2 > tw.fullwidth: + if linesofar: + tw.line(linesofar) + linesofar = ns + else: + if linesofar: + linesofar += ", " + ns + else: + linesofar = ns + if linesofar: + tw.line(linesofar) + tw.line("") + + + +oldbuiltins = {} + +def patch_builtins(assertion=True, compile=True): + """ put compile and AssertionError builtins to Python's builtins. """ + if assertion: + from py._code import assertion + l = oldbuiltins.setdefault('AssertionError', []) + l.append(py.builtin.builtins.AssertionError) + py.builtin.builtins.AssertionError = assertion.AssertionError + if compile: + l = oldbuiltins.setdefault('compile', []) + l.append(py.builtin.builtins.compile) + py.builtin.builtins.compile = py.code.compile + +def unpatch_builtins(assertion=True, compile=True): + """ remove compile and AssertionError builtins from Python builtins. """ + if assertion: + py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop() + if compile: + py.builtin.builtins.compile = oldbuiltins['compile'].pop() + +def getrawcode(obj, trycall=True): + """ return code object for given function. """ + try: + return obj.__code__ + except AttributeError: + obj = getattr(obj, 'im_func', obj) + obj = getattr(obj, 'func_code', obj) + obj = getattr(obj, 'f_code', obj) + obj = getattr(obj, '__code__', obj) + if trycall and not hasattr(obj, 'co_firstlineno'): + if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj): + x = getrawcode(obj.__call__, trycall=False) + if hasattr(x, 'co_firstlineno'): + return x + return obj + diff --git a/venv/lib/python3.5/site-packages/py/_code/source.py b/venv/lib/python3.5/site-packages/py/_code/source.py new file mode 100644 index 0000000..59281b5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_code/source.py @@ -0,0 +1,411 @@ +from __future__ import generators + +from bisect import bisect_right +import sys +import inspect, tokenize +import py +from types import ModuleType +cpy_compile = compile + +try: + import _ast + from _ast import PyCF_ONLY_AST as _AST_FLAG +except ImportError: + _AST_FLAG = 0 + _ast = None + + +class Source(object): + """ a immutable object holding a source code fragment, + possibly deindenting it. + """ + _compilecounter = 0 + def __init__(self, *parts, **kwargs): + self.lines = lines = [] + de = kwargs.get('deindent', True) + rstrip = kwargs.get('rstrip', True) + for part in parts: + if not part: + partlines = [] + if isinstance(part, Source): + partlines = part.lines + elif isinstance(part, (tuple, list)): + partlines = [x.rstrip("\n") for x in part] + elif isinstance(part, py.builtin._basestring): + partlines = part.split('\n') + if rstrip: + while partlines: + if partlines[-1].strip(): + break + partlines.pop() + else: + partlines = getsource(part, deindent=de).lines + if de: + partlines = deindent(partlines) + lines.extend(partlines) + + def __eq__(self, other): + try: + return self.lines == other.lines + except AttributeError: + if isinstance(other, str): + return str(self) == other + return False + + def __getitem__(self, key): + if isinstance(key, int): + return self.lines[key] + else: + if key.step not in (None, 1): + raise IndexError("cannot slice a Source with a step") + return self.__getslice__(key.start, key.stop) + + def __len__(self): + return len(self.lines) + + def __getslice__(self, start, end): + newsource = Source() + newsource.lines = self.lines[start:end] + return newsource + + def strip(self): + """ return new source object with trailing + and leading blank lines removed. + """ + start, end = 0, len(self) + while start < end and not self.lines[start].strip(): + start += 1 + while end > start and not self.lines[end-1].strip(): + end -= 1 + source = Source() + source.lines[:] = self.lines[start:end] + return source + + def putaround(self, before='', after='', indent=' ' * 4): + """ return a copy of the source object with + 'before' and 'after' wrapped around it. + """ + before = Source(before) + after = Source(after) + newsource = Source() + lines = [ (indent + line) for line in self.lines] + newsource.lines = before.lines + lines + after.lines + return newsource + + def indent(self, indent=' ' * 4): + """ return a copy of the source object with + all lines indented by the given indent-string. + """ + newsource = Source() + newsource.lines = [(indent+line) for line in self.lines] + return newsource + + def getstatement(self, lineno, assertion=False): + """ return Source statement which contains the + given linenumber (counted from 0). + """ + start, end = self.getstatementrange(lineno, assertion) + return self[start:end] + + def getstatementrange(self, lineno, assertion=False): + """ return (start, end) tuple which spans the minimal + statement region which containing the given lineno. + """ + if not (0 <= lineno < len(self)): + raise IndexError("lineno out of range") + ast, start, end = getstatementrange_ast(lineno, self) + return start, end + + def deindent(self, offset=None): + """ return a new source object deindented by offset. + If offset is None then guess an indentation offset from + the first non-blank line. Subsequent lines which have a + lower indentation offset will be copied verbatim as + they are assumed to be part of multilines. + """ + # XXX maybe use the tokenizer to properly handle multiline + # strings etc.pp? + newsource = Source() + newsource.lines[:] = deindent(self.lines, offset) + return newsource + + def isparseable(self, deindent=True): + """ return True if source is parseable, heuristically + deindenting it by default. + """ + try: + import parser + except ImportError: + syntax_checker = lambda x: compile(x, 'asd', 'exec') + else: + syntax_checker = parser.suite + + if deindent: + source = str(self.deindent()) + else: + source = str(self) + try: + #compile(source+'\n', "x", "exec") + syntax_checker(source+'\n') + except KeyboardInterrupt: + raise + except Exception: + return False + else: + return True + + def __str__(self): + return "\n".join(self.lines) + + def compile(self, filename=None, mode='exec', + flag=generators.compiler_flag, + dont_inherit=0, _genframe=None): + """ return compiled code object. if filename is None + invent an artificial filename which displays + the source/line position of the caller frame. + """ + if not filename or py.path.local(filename).check(file=0): + if _genframe is None: + _genframe = sys._getframe(1) # the caller + fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno + base = "<%d-codegen " % self._compilecounter + self.__class__._compilecounter += 1 + if not filename: + filename = base + '%s:%d>' % (fn, lineno) + else: + filename = base + '%r %s:%d>' % (filename, fn, lineno) + source = "\n".join(self.lines) + '\n' + try: + co = cpy_compile(source, filename, mode, flag) + except SyntaxError: + ex = sys.exc_info()[1] + # re-represent syntax errors from parsing python strings + msglines = self.lines[:ex.lineno] + if ex.offset: + msglines.append(" "*ex.offset + '^') + msglines.append("(code was compiled probably from here: %s)" % filename) + newex = SyntaxError('\n'.join(msglines)) + newex.offset = ex.offset + newex.lineno = ex.lineno + newex.text = ex.text + raise newex + else: + if flag & _AST_FLAG: + return co + lines = [(x + "\n") for x in self.lines] + py.std.linecache.cache[filename] = (1, None, lines, filename) + return co + +# +# public API shortcut functions +# + +def compile_(source, filename=None, mode='exec', flags= + generators.compiler_flag, dont_inherit=0): + """ compile the given source to a raw code object, + and maintain an internal cache which allows later + retrieval of the source code for the code object + and any recursively created code objects. + """ + if _ast is not None and isinstance(source, _ast.AST): + # XXX should Source support having AST? + return cpy_compile(source, filename, mode, flags, dont_inherit) + _genframe = sys._getframe(1) # the caller + s = Source(source) + co = s.compile(filename, mode, flags, _genframe=_genframe) + return co + + +def getfslineno(obj): + """ Return source location (path, lineno) for the given object. + If the source cannot be determined return ("", -1) + """ + try: + code = py.code.Code(obj) + except TypeError: + try: + fn = (py.std.inspect.getsourcefile(obj) or + py.std.inspect.getfile(obj)) + except TypeError: + return "", -1 + + fspath = fn and py.path.local(fn) or None + lineno = -1 + if fspath: + try: + _, lineno = findsource(obj) + except IOError: + pass + else: + fspath = code.path + lineno = code.firstlineno + assert isinstance(lineno, int) + return fspath, lineno + +# +# helper functions +# + +def findsource(obj): + try: + sourcelines, lineno = py.std.inspect.findsource(obj) + except py.builtin._sysex: + raise + except: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in sourcelines] + return source, lineno + +def getsource(obj, **kwargs): + obj = py.code.getrawcode(obj) + try: + strsrc = inspect.getsource(obj) + except IndentationError: + strsrc = "\"Buggy python version consider upgrading, cannot get source\"" + assert isinstance(strsrc, str) + return Source(strsrc, **kwargs) + +def deindent(lines, offset=None): + if offset is None: + for line in lines: + line = line.expandtabs() + s = line.lstrip() + if s: + offset = len(line)-len(s) + break + else: + offset = 0 + if offset == 0: + return list(lines) + newlines = [] + def readline_generator(lines): + for line in lines: + yield line + '\n' + while True: + yield '' + + it = readline_generator(lines) + + try: + for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)): + if sline > len(lines): + break # End of input reached + if sline > len(newlines): + line = lines[sline - 1].expandtabs() + if line.lstrip() and line[:offset].isspace(): + line = line[offset:] # Deindent + newlines.append(line) + + for i in range(sline, eline): + # Don't deindent continuing lines of + # multiline tokens (i.e. multiline strings) + newlines.append(lines[i]) + except (IndentationError, tokenize.TokenError): + pass + # Add any lines we didn't see. E.g. if an exception was raised. + newlines.extend(lines[len(newlines):]) + return newlines + + +def get_statement_startend2(lineno, node): + import ast + # flatten all statements and except handlers into one lineno-list + # AST's line numbers start indexing at 1 + l = [] + for x in ast.walk(node): + if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler): + l.append(x.lineno - 1) + for name in "finalbody", "orelse": + val = getattr(x, name, None) + if val: + # treat the finally/orelse part as its own statement + l.append(val[0].lineno - 1 - 1) + l.sort() + insert_index = bisect_right(l, lineno) + start = l[insert_index - 1] + if insert_index >= len(l): + end = None + else: + end = l[insert_index] + return start, end + + +def getstatementrange_ast(lineno, source, assertion=False, astnode=None): + if astnode is None: + content = str(source) + if sys.version_info < (2,7): + content += "\n" + try: + astnode = compile(content, "source", "exec", 1024) # 1024 for AST + except ValueError: + start, end = getstatementrange_old(lineno, source, assertion) + return None, start, end + start, end = get_statement_startend2(lineno, astnode) + # we need to correct the end: + # - ast-parsing strips comments + # - there might be empty lines + # - we might have lesser indented code blocks at the end + if end is None: + end = len(source.lines) + + if end > start + 1: + # make sure we don't span differently indented code blocks + # by using the BlockFinder helper used which inspect.getsource() uses itself + block_finder = inspect.BlockFinder() + # if we start with an indented line, put blockfinder to "started" mode + block_finder.started = source.lines[start][0].isspace() + it = ((x + "\n") for x in source.lines[start:end]) + try: + for tok in tokenize.generate_tokens(lambda: next(it)): + block_finder.tokeneater(*tok) + except (inspect.EndOfBlock, IndentationError): + end = block_finder.last + start + except Exception: + pass + + # the end might still point to a comment or empty line, correct it + while end: + line = source.lines[end - 1].lstrip() + if line.startswith("#") or not line: + end -= 1 + else: + break + return astnode, start, end + + +def getstatementrange_old(lineno, source, assertion=False): + """ return (start, end) tuple which spans the minimal + statement region which containing the given lineno. + raise an IndexError if no such statementrange can be found. + """ + # XXX this logic is only used on python2.4 and below + # 1. find the start of the statement + from codeop import compile_command + for start in range(lineno, -1, -1): + if assertion: + line = source.lines[start] + # the following lines are not fully tested, change with care + if 'super' in line and 'self' in line and '__init__' in line: + raise IndexError("likely a subclass") + if "assert" not in line and "raise" not in line: + continue + trylines = source.lines[start:lineno+1] + # quick hack to prepare parsing an indented line with + # compile_command() (which errors on "return" outside defs) + trylines.insert(0, 'def xxx():') + trysource = '\n '.join(trylines) + # ^ space here + try: + compile_command(trysource) + except (SyntaxError, OverflowError, ValueError): + continue + + # 2. find the end of the statement + for end in range(lineno+1, len(source)+1): + trysource = source[start:end] + if trysource.isparseable(): + return start, end + raise SyntaxError("no valid source range around line %d " % (lineno,)) + + diff --git a/venv/lib/python3.5/site-packages/py/_error.py b/venv/lib/python3.5/site-packages/py/_error.py new file mode 100644 index 0000000..228de58 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_error.py @@ -0,0 +1,89 @@ +""" +create errno-specific classes for IO or os calls. + +""" +import sys, os, errno + +class Error(EnvironmentError): + def __repr__(self): + return "%s.%s %r: %s " %(self.__class__.__module__, + self.__class__.__name__, + self.__class__.__doc__, + " ".join(map(str, self.args)), + #repr(self.args) + ) + + def __str__(self): + s = "[%s]: %s" %(self.__class__.__doc__, + " ".join(map(str, self.args)), + ) + return s + +_winerrnomap = { + 2: errno.ENOENT, + 3: errno.ENOENT, + 17: errno.EEXIST, + 18: errno.EXDEV, + 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable + 22: errno.ENOTDIR, + 20: errno.ENOTDIR, + 267: errno.ENOTDIR, + 5: errno.EACCES, # anything better? +} + +class ErrorMaker(object): + """ lazily provides Exception classes for each possible POSIX errno + (as defined per the 'errno' module). All such instances + subclass EnvironmentError. + """ + Error = Error + _errno2class = {} + + def __getattr__(self, name): + if name[0] == "_": + raise AttributeError(name) + eno = getattr(errno, name) + cls = self._geterrnoclass(eno) + setattr(self, name, cls) + return cls + + def _geterrnoclass(self, eno): + try: + return self._errno2class[eno] + except KeyError: + clsname = errno.errorcode.get(eno, "UnknownErrno%d" %(eno,)) + errorcls = type(Error)(clsname, (Error,), + {'__module__':'py.error', + '__doc__': os.strerror(eno)}) + self._errno2class[eno] = errorcls + return errorcls + + def checked_call(self, func, *args, **kwargs): + """ call a function and raise an errno-exception if applicable. """ + __tracebackhide__ = True + try: + return func(*args, **kwargs) + except self.Error: + raise + except (OSError, EnvironmentError): + cls, value, tb = sys.exc_info() + if not hasattr(value, 'errno'): + raise + __tracebackhide__ = False + errno = value.errno + try: + if not isinstance(value, WindowsError): + raise NameError + except NameError: + # we are not on Windows, or we got a proper OSError + cls = self._geterrnoclass(errno) + else: + try: + cls = self._geterrnoclass(_winerrnomap[errno]) + except KeyError: + raise value + raise cls("%s%r" % (func.__name__, args)) + __tracebackhide__ = True + + +error = ErrorMaker() diff --git a/venv/lib/python3.5/site-packages/py/_iniconfig.py b/venv/lib/python3.5/site-packages/py/_iniconfig.py new file mode 100644 index 0000000..8e46404 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_iniconfig.py @@ -0,0 +1,162 @@ +""" brain-dead simple parser for ini-style files. +(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed +""" +__version__ = "0.2.dev2" + +__all__ = ['IniConfig', 'ParseError'] + +COMMENTCHARS = "#;" + +class ParseError(Exception): + def __init__(self, path, lineno, msg): + Exception.__init__(self, path, lineno, msg) + self.path = path + self.lineno = lineno + self.msg = msg + + def __str__(self): + return "%s:%s: %s" %(self.path, self.lineno+1, self.msg) + +class SectionWrapper(object): + def __init__(self, config, name): + self.config = config + self.name = name + + def lineof(self, name): + return self.config.lineof(self.name, name) + + def get(self, key, default=None, convert=str): + return self.config.get(self.name, key, convert=convert, default=default) + + def __getitem__(self, key): + return self.config.sections[self.name][key] + + def __iter__(self): + section = self.config.sections.get(self.name, []) + def lineof(key): + return self.config.lineof(self.name, key) + for name in sorted(section, key=lineof): + yield name + + def items(self): + for name in self: + yield name, self[name] + + +class IniConfig(object): + def __init__(self, path, data=None): + self.path = str(path) # convenience + if data is None: + f = open(self.path) + try: + tokens = self._parse(iter(f)) + finally: + f.close() + else: + tokens = self._parse(data.splitlines(True)) + + self._sources = {} + self.sections = {} + + for lineno, section, name, value in tokens: + if section is None: + self._raise(lineno, 'no section header defined') + self._sources[section, name] = lineno + if name is None: + if section in self.sections: + self._raise(lineno, 'duplicate section %r'%(section, )) + self.sections[section] = {} + else: + if name in self.sections[section]: + self._raise(lineno, 'duplicate name %r'%(name, )) + self.sections[section][name] = value + + def _raise(self, lineno, msg): + raise ParseError(self.path, lineno, msg) + + def _parse(self, line_iter): + result = [] + section = None + for lineno, line in enumerate(line_iter): + name, data = self._parseline(line, lineno) + # new value + if name is not None and data is not None: + result.append((lineno, section, name, data)) + # new section + elif name is not None and data is None: + if not name: + self._raise(lineno, 'empty section name') + section = name + result.append((lineno, section, None, None)) + # continuation + elif name is None and data is not None: + if not result: + self._raise(lineno, 'unexpected value continuation') + last = result.pop() + last_name, last_data = last[-2:] + if last_name is None: + self._raise(lineno, 'unexpected value continuation') + + if last_data: + data = '%s\n%s' % (last_data, data) + result.append(last[:-1] + (data,)) + return result + + def _parseline(self, line, lineno): + # blank lines + if iscommentline(line): + line = "" + else: + line = line.rstrip() + if not line: + return None, None + # section + if line[0] == '[': + realline = line + for c in COMMENTCHARS: + line = line.split(c)[0].rstrip() + if line[-1] == "]": + return line[1:-1], None + return None, realline.strip() + # value + elif not line[0].isspace(): + try: + name, value = line.split('=', 1) + if ":" in name: + raise ValueError() + except ValueError: + try: + name, value = line.split(":", 1) + except ValueError: + self._raise(lineno, 'unexpected line: %r' % line) + return name.strip(), value.strip() + # continuation + else: + return None, line.strip() + + def lineof(self, section, name=None): + lineno = self._sources.get((section, name)) + if lineno is not None: + return lineno + 1 + + def get(self, section, name, default=None, convert=str): + try: + return convert(self.sections[section][name]) + except KeyError: + return default + + def __getitem__(self, name): + if name not in self.sections: + raise KeyError(name) + return SectionWrapper(self, name) + + def __iter__(self): + for name in sorted(self.sections, key=self.lineof): + yield SectionWrapper(self, name) + + def __contains__(self, arg): + return arg in self.sections + +def iscommentline(line): + c = line.lstrip()[:1] + return c in COMMENTCHARS diff --git a/venv/lib/python3.5/site-packages/py/_io/__init__.py b/venv/lib/python3.5/site-packages/py/_io/__init__.py new file mode 100644 index 0000000..f1a6d63 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_io/__init__.py @@ -0,0 +1 @@ +""" input/output helping """ diff --git a/venv/lib/python3.5/site-packages/py/_io/capture.py b/venv/lib/python3.5/site-packages/py/_io/capture.py new file mode 100644 index 0000000..3ce2259 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_io/capture.py @@ -0,0 +1,371 @@ +import os +import sys +import py +import tempfile + +try: + from io import StringIO +except ImportError: + from StringIO import StringIO + +if sys.version_info < (3,0): + class TextIO(StringIO): + def write(self, data): + if not isinstance(data, unicode): + data = unicode(data, getattr(self, '_encoding', 'UTF-8'), 'replace') + StringIO.write(self, data) +else: + TextIO = StringIO + +try: + from io import BytesIO +except ImportError: + class BytesIO(StringIO): + def write(self, data): + if isinstance(data, unicode): + raise TypeError("not a byte value: %r" %(data,)) + StringIO.write(self, data) + +patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} + +class FDCapture: + """ Capture IO to/from a given os-level filedescriptor. """ + + def __init__(self, targetfd, tmpfile=None, now=True, patchsys=False): + """ save targetfd descriptor, and open a new + temporary file there. If no tmpfile is + specified a tempfile.Tempfile() will be opened + in text mode. + """ + self.targetfd = targetfd + if tmpfile is None and targetfd != 0: + f = tempfile.TemporaryFile('wb+') + tmpfile = dupfile(f, encoding="UTF-8") + f.close() + self.tmpfile = tmpfile + self._savefd = os.dup(self.targetfd) + if patchsys: + self._oldsys = getattr(sys, patchsysdict[targetfd]) + if now: + self.start() + + def start(self): + try: + os.fstat(self._savefd) + except OSError: + raise ValueError("saved filedescriptor not valid, " + "did you call start() twice?") + if self.targetfd == 0 and not self.tmpfile: + fd = os.open(devnullpath, os.O_RDONLY) + os.dup2(fd, 0) + os.close(fd) + if hasattr(self, '_oldsys'): + setattr(sys, patchsysdict[self.targetfd], DontReadFromInput()) + else: + os.dup2(self.tmpfile.fileno(), self.targetfd) + if hasattr(self, '_oldsys'): + setattr(sys, patchsysdict[self.targetfd], self.tmpfile) + + def done(self): + """ unpatch and clean up, returns the self.tmpfile (file object) + """ + os.dup2(self._savefd, self.targetfd) + os.close(self._savefd) + if self.targetfd != 0: + self.tmpfile.seek(0) + if hasattr(self, '_oldsys'): + setattr(sys, patchsysdict[self.targetfd], self._oldsys) + return self.tmpfile + + def writeorg(self, data): + """ write a string to the original file descriptor + """ + tempfp = tempfile.TemporaryFile() + try: + os.dup2(self._savefd, tempfp.fileno()) + tempfp.write(data) + finally: + tempfp.close() + + +def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): + """ return a new open file object that's a duplicate of f + + mode is duplicated if not given, 'buffering' controls + buffer size (defaulting to no buffering) and 'raising' + defines whether an exception is raised when an incompatible + file object is passed in (if raising is False, the file + object itself will be returned) + """ + try: + fd = f.fileno() + mode = mode or f.mode + except AttributeError: + if raising: + raise + return f + newfd = os.dup(fd) + if sys.version_info >= (3,0): + if encoding is not None: + mode = mode.replace("b", "") + buffering = True + return os.fdopen(newfd, mode, buffering, encoding, closefd=True) + else: + f = os.fdopen(newfd, mode, buffering) + if encoding is not None: + return EncodedFile(f, encoding) + return f + +class EncodedFile(object): + def __init__(self, _stream, encoding): + self._stream = _stream + self.encoding = encoding + + def write(self, obj): + if isinstance(obj, unicode): + obj = obj.encode(self.encoding) + elif isinstance(obj, str): + pass + else: + obj = str(obj) + self._stream.write(obj) + + def writelines(self, linelist): + data = ''.join(linelist) + self.write(data) + + def __getattr__(self, name): + return getattr(self._stream, name) + +class Capture(object): + def call(cls, func, *args, **kwargs): + """ return a (res, out, err) tuple where + out and err represent the output/error output + during function execution. + call the given function with args/kwargs + and capture output/error during its execution. + """ + so = cls() + try: + res = func(*args, **kwargs) + finally: + out, err = so.reset() + return res, out, err + call = classmethod(call) + + def reset(self): + """ reset sys.stdout/stderr and return captured output as strings. """ + if hasattr(self, '_reset'): + raise ValueError("was already reset") + self._reset = True + outfile, errfile = self.done(save=False) + out, err = "", "" + if outfile and not outfile.closed: + out = outfile.read() + outfile.close() + if errfile and errfile != outfile and not errfile.closed: + err = errfile.read() + errfile.close() + return out, err + + def suspend(self): + """ return current snapshot captures, memorize tempfiles. """ + outerr = self.readouterr() + outfile, errfile = self.done() + return outerr + + +class StdCaptureFD(Capture): + """ This class allows to capture writes to FD1 and FD2 + and may connect a NULL file to FD0 (and prevent + reads from sys.stdin). If any of the 0,1,2 file descriptors + is invalid it will not be captured. + """ + def __init__(self, out=True, err=True, mixed=False, + in_=True, patchsys=True, now=True): + self._options = { + "out": out, + "err": err, + "mixed": mixed, + "in_": in_, + "patchsys": patchsys, + "now": now, + } + self._save() + if now: + self.startall() + + def _save(self): + in_ = self._options['in_'] + out = self._options['out'] + err = self._options['err'] + mixed = self._options['mixed'] + patchsys = self._options['patchsys'] + if in_: + try: + self.in_ = FDCapture(0, tmpfile=None, now=False, + patchsys=patchsys) + except OSError: + pass + if out: + tmpfile = None + if hasattr(out, 'write'): + tmpfile = out + try: + self.out = FDCapture(1, tmpfile=tmpfile, + now=False, patchsys=patchsys) + self._options['out'] = self.out.tmpfile + except OSError: + pass + if err: + if out and mixed: + tmpfile = self.out.tmpfile + elif hasattr(err, 'write'): + tmpfile = err + else: + tmpfile = None + try: + self.err = FDCapture(2, tmpfile=tmpfile, + now=False, patchsys=patchsys) + self._options['err'] = self.err.tmpfile + except OSError: + pass + + def startall(self): + if hasattr(self, 'in_'): + self.in_.start() + if hasattr(self, 'out'): + self.out.start() + if hasattr(self, 'err'): + self.err.start() + + def resume(self): + """ resume capturing with original temp files. """ + self.startall() + + def done(self, save=True): + """ return (outfile, errfile) and stop capturing. """ + outfile = errfile = None + if hasattr(self, 'out') and not self.out.tmpfile.closed: + outfile = self.out.done() + if hasattr(self, 'err') and not self.err.tmpfile.closed: + errfile = self.err.done() + if hasattr(self, 'in_'): + tmpfile = self.in_.done() + if save: + self._save() + return outfile, errfile + + def readouterr(self): + """ return snapshot value of stdout/stderr capturings. """ + if hasattr(self, "out"): + out = self._readsnapshot(self.out.tmpfile) + else: + out = "" + if hasattr(self, "err"): + err = self._readsnapshot(self.err.tmpfile) + else: + err = "" + return [out, err] + + def _readsnapshot(self, f): + f.seek(0) + res = f.read() + enc = getattr(f, "encoding", None) + if enc: + res = py.builtin._totext(res, enc, "replace") + f.truncate(0) + f.seek(0) + return res + + +class StdCapture(Capture): + """ This class allows to capture writes to sys.stdout|stderr "in-memory" + and will raise errors on tries to read from sys.stdin. It only + modifies sys.stdout|stderr|stdin attributes and does not + touch underlying File Descriptors (use StdCaptureFD for that). + """ + def __init__(self, out=True, err=True, in_=True, mixed=False, now=True): + self._oldout = sys.stdout + self._olderr = sys.stderr + self._oldin = sys.stdin + if out and not hasattr(out, 'file'): + out = TextIO() + self.out = out + if err: + if mixed: + err = out + elif not hasattr(err, 'write'): + err = TextIO() + self.err = err + self.in_ = in_ + if now: + self.startall() + + def startall(self): + if self.out: + sys.stdout = self.out + if self.err: + sys.stderr = self.err + if self.in_: + sys.stdin = self.in_ = DontReadFromInput() + + def done(self, save=True): + """ return (outfile, errfile) and stop capturing. """ + outfile = errfile = None + if self.out and not self.out.closed: + sys.stdout = self._oldout + outfile = self.out + outfile.seek(0) + if self.err and not self.err.closed: + sys.stderr = self._olderr + errfile = self.err + errfile.seek(0) + if self.in_: + sys.stdin = self._oldin + return outfile, errfile + + def resume(self): + """ resume capturing with original temp files. """ + self.startall() + + def readouterr(self): + """ return snapshot value of stdout/stderr capturings. """ + out = err = "" + if self.out: + out = self.out.getvalue() + self.out.truncate(0) + self.out.seek(0) + if self.err: + err = self.err.getvalue() + self.err.truncate(0) + self.err.seek(0) + return out, err + +class DontReadFromInput: + """Temporary stub class. Ideally when stdin is accessed, the + capturing should be turned off, with possibly all data captured + so far sent to the screen. This should be configurable, though, + because in automated test runs it is better to crash than + hang indefinitely. + """ + def read(self, *args): + raise IOError("reading from stdin while output is captured") + readline = read + readlines = read + __iter__ = read + + def fileno(self): + raise ValueError("redirected Stdin is pseudofile, has no fileno()") + def isatty(self): + return False + def close(self): + pass + +try: + devnullpath = os.devnull +except AttributeError: + if os.name == 'nt': + devnullpath = 'NUL' + else: + devnullpath = '/dev/null' diff --git a/venv/lib/python3.5/site-packages/py/_io/saferepr.py b/venv/lib/python3.5/site-packages/py/_io/saferepr.py new file mode 100644 index 0000000..e86f18f --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_io/saferepr.py @@ -0,0 +1,71 @@ +import py +import sys + +builtin_repr = repr + +reprlib = py.builtin._tryimport('repr', 'reprlib') + +class SafeRepr(reprlib.Repr): + """ subclass of repr.Repr that limits the resulting size of repr() + and includes information on exceptions raised during the call. + """ + def repr(self, x): + return self._callhelper(reprlib.Repr.repr, self, x) + + def repr_unicode(self, x, level): + # Strictly speaking wrong on narrow builds + def repr(u): + if "'" not in u: + return py.builtin._totext("'%s'") % u + elif '"' not in u: + return py.builtin._totext('"%s"') % u + else: + return py.builtin._totext("'%s'") % u.replace("'", r"\'") + s = repr(x[:self.maxstring]) + if len(s) > self.maxstring: + i = max(0, (self.maxstring-3)//2) + j = max(0, self.maxstring-3-i) + s = repr(x[:i] + x[len(x)-j:]) + s = s[:i] + '...' + s[len(s)-j:] + return s + + def repr_instance(self, x, level): + return self._callhelper(builtin_repr, x) + + def _callhelper(self, call, x, *args): + try: + # Try the vanilla repr and make sure that the result is a string + s = call(x, *args) + except py.builtin._sysex: + raise + except: + cls, e, tb = sys.exc_info() + exc_name = getattr(cls, '__name__', 'unknown') + try: + exc_info = str(e) + except py.builtin._sysex: + raise + except: + exc_info = 'unknown' + return '<[%s("%s") raised in repr()] %s object at 0x%x>' % ( + exc_name, exc_info, x.__class__.__name__, id(x)) + else: + if len(s) > self.maxsize: + i = max(0, (self.maxsize-3)//2) + j = max(0, self.maxsize-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + +def saferepr(obj, maxsize=240): + """ return a size-limited safe repr-string for the given object. + Failing __repr__ functions of user instances will be represented + with a short exception info and 'saferepr' generally takes + care to never raise exceptions itself. This function is a wrapper + around the Repr/reprlib functionality of the standard 2.6 lib. + """ + # review exception handling + srepr = SafeRepr() + srepr.maxstring = maxsize + srepr.maxsize = maxsize + srepr.maxother = 160 + return srepr.repr(obj) diff --git a/venv/lib/python3.5/site-packages/py/_io/terminalwriter.py b/venv/lib/python3.5/site-packages/py/_io/terminalwriter.py new file mode 100644 index 0000000..715547d --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_io/terminalwriter.py @@ -0,0 +1,357 @@ +""" + +Helper functions for writing to terminals and files. + +""" + + +import sys, os +import py +py3k = sys.version_info[0] >= 3 +from py.builtin import text, bytes + +win32_and_ctypes = False +colorama = None +if sys.platform == "win32": + try: + import colorama + except ImportError: + try: + import ctypes + win32_and_ctypes = True + except ImportError: + pass + + +def _getdimensions(): + import termios,fcntl,struct + call = fcntl.ioctl(1,termios.TIOCGWINSZ,"\000"*8) + height,width = struct.unpack( "hhhh", call ) [:2] + return height, width + + +def get_terminal_width(): + height = width = 0 + try: + height, width = _getdimensions() + except py.builtin._sysex: + raise + except: + # pass to fallback below + pass + + if width == 0: + # FALLBACK: + # * some exception happened + # * or this is emacs terminal which reports (0,0) + width = int(os.environ.get('COLUMNS', 80)) + + # XXX the windows getdimensions may be bogus, let's sanify a bit + if width < 40: + width = 80 + return width + +terminal_width = get_terminal_width() + +# XXX unify with _escaped func below +def ansi_print(text, esc, file=None, newline=True, flush=False): + if file is None: + file = sys.stderr + text = text.rstrip() + if esc and not isinstance(esc, tuple): + esc = (esc,) + if esc and sys.platform != "win32" and file.isatty(): + text = (''.join(['\x1b[%sm' % cod for cod in esc]) + + text + + '\x1b[0m') # ANSI color code "reset" + if newline: + text += '\n' + + if esc and win32_and_ctypes and file.isatty(): + if 1 in esc: + bold = True + esc = tuple([x for x in esc if x != 1]) + else: + bold = False + esctable = {() : FOREGROUND_WHITE, # normal + (31,): FOREGROUND_RED, # red + (32,): FOREGROUND_GREEN, # green + (33,): FOREGROUND_GREEN|FOREGROUND_RED, # yellow + (34,): FOREGROUND_BLUE, # blue + (35,): FOREGROUND_BLUE|FOREGROUND_RED, # purple + (36,): FOREGROUND_BLUE|FOREGROUND_GREEN, # cyan + (37,): FOREGROUND_WHITE, # white + (39,): FOREGROUND_WHITE, # reset + } + attr = esctable.get(esc, FOREGROUND_WHITE) + if bold: + attr |= FOREGROUND_INTENSITY + STD_OUTPUT_HANDLE = -11 + STD_ERROR_HANDLE = -12 + if file is sys.stderr: + handle = GetStdHandle(STD_ERROR_HANDLE) + else: + handle = GetStdHandle(STD_OUTPUT_HANDLE) + oldcolors = GetConsoleInfo(handle).wAttributes + attr |= (oldcolors & 0x0f0) + SetConsoleTextAttribute(handle, attr) + while len(text) > 32768: + file.write(text[:32768]) + text = text[32768:] + if text: + file.write(text) + SetConsoleTextAttribute(handle, oldcolors) + else: + file.write(text) + + if flush: + file.flush() + +def should_do_markup(file): + if os.environ.get('PY_COLORS') == '1': + return True + if os.environ.get('PY_COLORS') == '0': + return False + return hasattr(file, 'isatty') and file.isatty() \ + and os.environ.get('TERM') != 'dumb' \ + and not (sys.platform.startswith('java') and os._name == 'nt') + +class TerminalWriter(object): + _esctable = dict(black=30, red=31, green=32, yellow=33, + blue=34, purple=35, cyan=36, white=37, + Black=40, Red=41, Green=42, Yellow=43, + Blue=44, Purple=45, Cyan=46, White=47, + bold=1, light=2, blink=5, invert=7) + + # XXX deprecate stringio argument + def __init__(self, file=None, stringio=False, encoding=None): + if file is None: + if stringio: + self.stringio = file = py.io.TextIO() + else: + file = py.std.sys.stdout + elif py.builtin.callable(file) and not ( + hasattr(file, "write") and hasattr(file, "flush")): + file = WriteFile(file, encoding=encoding) + if hasattr(file, "isatty") and file.isatty() and colorama: + file = colorama.AnsiToWin32(file).stream + self.encoding = encoding or getattr(file, 'encoding', "utf-8") + self._file = file + self.hasmarkup = should_do_markup(file) + self._lastlen = 0 + + @property + def fullwidth(self): + if hasattr(self, '_terminal_width'): + return self._terminal_width + return get_terminal_width() + + @fullwidth.setter + def fullwidth(self, value): + self._terminal_width = value + + def _escaped(self, text, esc): + if esc and self.hasmarkup: + text = (''.join(['\x1b[%sm' % cod for cod in esc]) + + text +'\x1b[0m') + return text + + def markup(self, text, **kw): + esc = [] + for name in kw: + if name not in self._esctable: + raise ValueError("unknown markup: %r" %(name,)) + if kw[name]: + esc.append(self._esctable[name]) + return self._escaped(text, tuple(esc)) + + def sep(self, sepchar, title=None, fullwidth=None, **kw): + if fullwidth is None: + fullwidth = self.fullwidth + # the goal is to have the line be as long as possible + # under the condition that len(line) <= fullwidth + if sys.platform == "win32": + # if we print in the last column on windows we are on a + # new line but there is no way to verify/neutralize this + # (we may not know the exact line width) + # so let's be defensive to avoid empty lines in the output + fullwidth -= 1 + if title is not None: + # we want 2 + 2*len(fill) + len(title) <= fullwidth + # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth + # 2*len(sepchar)*N <= fullwidth - len(title) - 2 + # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) + N = (fullwidth - len(title) - 2) // (2*len(sepchar)) + fill = sepchar * N + line = "%s %s %s" % (fill, title, fill) + else: + # we want len(sepchar)*N <= fullwidth + # i.e. N <= fullwidth // len(sepchar) + line = sepchar * (fullwidth // len(sepchar)) + # in some situations there is room for an extra sepchar at the right, + # in particular if we consider that with a sepchar like "_ " the + # trailing space is not important at the end of the line + if len(line) + len(sepchar.rstrip()) <= fullwidth: + line += sepchar.rstrip() + + self.line(line, **kw) + + def write(self, msg, **kw): + if msg: + if not isinstance(msg, (bytes, text)): + msg = text(msg) + if self.hasmarkup and kw: + markupmsg = self.markup(msg, **kw) + else: + markupmsg = msg + write_out(self._file, markupmsg) + + def line(self, s='', **kw): + self.write(s, **kw) + self._checkfill(s) + self.write('\n') + + def reline(self, line, **kw): + if not self.hasmarkup: + raise ValueError("cannot use rewrite-line without terminal") + self.write(line, **kw) + self._checkfill(line) + self.write('\r') + self._lastlen = len(line) + + def _checkfill(self, line): + diff2last = self._lastlen - len(line) + if diff2last > 0: + self.write(" " * diff2last) + +class Win32ConsoleWriter(TerminalWriter): + def write(self, msg, **kw): + if msg: + if not isinstance(msg, (bytes, text)): + msg = text(msg) + oldcolors = None + if self.hasmarkup and kw: + handle = GetStdHandle(STD_OUTPUT_HANDLE) + oldcolors = GetConsoleInfo(handle).wAttributes + default_bg = oldcolors & 0x00F0 + attr = default_bg + if kw.pop('bold', False): + attr |= FOREGROUND_INTENSITY + + if kw.pop('red', False): + attr |= FOREGROUND_RED + elif kw.pop('blue', False): + attr |= FOREGROUND_BLUE + elif kw.pop('green', False): + attr |= FOREGROUND_GREEN + elif kw.pop('yellow', False): + attr |= FOREGROUND_GREEN|FOREGROUND_RED + else: + attr |= oldcolors & 0x0007 + + SetConsoleTextAttribute(handle, attr) + write_out(self._file, msg) + if oldcolors: + SetConsoleTextAttribute(handle, oldcolors) + +class WriteFile(object): + def __init__(self, writemethod, encoding=None): + self.encoding = encoding + self._writemethod = writemethod + + def write(self, data): + if self.encoding: + data = data.encode(self.encoding, "replace") + self._writemethod(data) + + def flush(self): + return + + +if win32_and_ctypes: + TerminalWriter = Win32ConsoleWriter + import ctypes + from ctypes import wintypes + + # ctypes access to the Windows console + STD_OUTPUT_HANDLE = -11 + STD_ERROR_HANDLE = -12 + FOREGROUND_BLACK = 0x0000 # black text + FOREGROUND_BLUE = 0x0001 # text color contains blue. + FOREGROUND_GREEN = 0x0002 # text color contains green. + FOREGROUND_RED = 0x0004 # text color contains red. + FOREGROUND_WHITE = 0x0007 + FOREGROUND_INTENSITY = 0x0008 # text color is intensified. + BACKGROUND_BLACK = 0x0000 # background color black + BACKGROUND_BLUE = 0x0010 # background color contains blue. + BACKGROUND_GREEN = 0x0020 # background color contains green. + BACKGROUND_RED = 0x0040 # background color contains red. + BACKGROUND_WHITE = 0x0070 + BACKGROUND_INTENSITY = 0x0080 # background color is intensified. + + SHORT = ctypes.c_short + class COORD(ctypes.Structure): + _fields_ = [('X', SHORT), + ('Y', SHORT)] + class SMALL_RECT(ctypes.Structure): + _fields_ = [('Left', SHORT), + ('Top', SHORT), + ('Right', SHORT), + ('Bottom', SHORT)] + class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): + _fields_ = [('dwSize', COORD), + ('dwCursorPosition', COORD), + ('wAttributes', wintypes.WORD), + ('srWindow', SMALL_RECT), + ('dwMaximumWindowSize', COORD)] + + _GetStdHandle = ctypes.windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [wintypes.DWORD] + _GetStdHandle.restype = wintypes.HANDLE + def GetStdHandle(kind): + return _GetStdHandle(kind) + + SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute + SetConsoleTextAttribute.argtypes = [wintypes.HANDLE, wintypes.WORD] + SetConsoleTextAttribute.restype = wintypes.BOOL + + _GetConsoleScreenBufferInfo = \ + ctypes.windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [wintypes.HANDLE, + ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + def GetConsoleInfo(handle): + info = CONSOLE_SCREEN_BUFFER_INFO() + _GetConsoleScreenBufferInfo(handle, ctypes.byref(info)) + return info + + def _getdimensions(): + handle = GetStdHandle(STD_OUTPUT_HANDLE) + info = GetConsoleInfo(handle) + # Substract one from the width, otherwise the cursor wraps + # and the ending \n causes an empty line to display. + return info.dwSize.Y, info.dwSize.X - 1 + +def write_out(fil, msg): + # XXX sometimes "msg" is of type bytes, sometimes text which + # complicates the situation. Should we try to enforce unicode? + try: + # on py27 and above writing out to sys.stdout with an encoding + # should usually work for unicode messages (if the encoding is + # capable of it) + fil.write(msg) + except UnicodeEncodeError: + # on py26 it might not work because stdout expects bytes + if fil.encoding: + try: + fil.write(msg.encode(fil.encoding)) + except UnicodeEncodeError: + # it might still fail if the encoding is not capable + pass + else: + fil.flush() + return + # fallback: escape all unicode characters + msg = msg.encode("unicode-escape").decode("ascii") + fil.write(msg) + fil.flush() diff --git a/venv/lib/python3.5/site-packages/py/_log/__init__.py b/venv/lib/python3.5/site-packages/py/_log/__init__.py new file mode 100644 index 0000000..b211e2d --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_log/__init__.py @@ -0,0 +1,2 @@ +""" logging API ('producers' and 'consumers' connected via keywords) """ + diff --git a/venv/lib/python3.5/site-packages/py/_log/log.py b/venv/lib/python3.5/site-packages/py/_log/log.py new file mode 100644 index 0000000..86205fe --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_log/log.py @@ -0,0 +1,186 @@ +""" +basic logging functionality based on a producer/consumer scheme. + +XXX implement this API: (maybe put it into slogger.py?) + + log = Logger( + info=py.log.STDOUT, + debug=py.log.STDOUT, + command=None) + log.info("hello", "world") + log.command("hello", "world") + + log = Logger(info=Logger(something=...), + debug=py.log.STDOUT, + command=None) +""" +import py, sys + +class Message(object): + def __init__(self, keywords, args): + self.keywords = keywords + self.args = args + + def content(self): + return " ".join(map(str, self.args)) + + def prefix(self): + return "[%s] " % (":".join(self.keywords)) + + def __str__(self): + return self.prefix() + self.content() + + +class Producer(object): + """ (deprecated) Log producer API which sends messages to be logged + to a 'consumer' object, which then prints them to stdout, + stderr, files, etc. Used extensively by PyPy-1.1. + """ + + Message = Message # to allow later customization + keywords2consumer = {} + + def __init__(self, keywords, keywordmapper=None, **kw): + if hasattr(keywords, 'split'): + keywords = tuple(keywords.split()) + self._keywords = keywords + if keywordmapper is None: + keywordmapper = default_keywordmapper + self._keywordmapper = keywordmapper + + def __repr__(self): + return "<py.log.Producer %s>" % ":".join(self._keywords) + + def __getattr__(self, name): + if '_' in name: + raise AttributeError(name) + producer = self.__class__(self._keywords + (name,)) + setattr(self, name, producer) + return producer + + def __call__(self, *args): + """ write a message to the appropriate consumer(s) """ + func = self._keywordmapper.getconsumer(self._keywords) + if func is not None: + func(self.Message(self._keywords, args)) + +class KeywordMapper: + def __init__(self): + self.keywords2consumer = {} + + def getstate(self): + return self.keywords2consumer.copy() + def setstate(self, state): + self.keywords2consumer.clear() + self.keywords2consumer.update(state) + + def getconsumer(self, keywords): + """ return a consumer matching the given keywords. + + tries to find the most suitable consumer by walking, starting from + the back, the list of keywords, the first consumer matching a + keyword is returned (falling back to py.log.default) + """ + for i in range(len(keywords), 0, -1): + try: + return self.keywords2consumer[keywords[:i]] + except KeyError: + continue + return self.keywords2consumer.get('default', default_consumer) + + def setconsumer(self, keywords, consumer): + """ set a consumer for a set of keywords. """ + # normalize to tuples + if isinstance(keywords, str): + keywords = tuple(filter(None, keywords.split())) + elif hasattr(keywords, '_keywords'): + keywords = keywords._keywords + elif not isinstance(keywords, tuple): + raise TypeError("key %r is not a string or tuple" % (keywords,)) + if consumer is not None and not py.builtin.callable(consumer): + if not hasattr(consumer, 'write'): + raise TypeError( + "%r should be None, callable or file-like" % (consumer,)) + consumer = File(consumer) + self.keywords2consumer[keywords] = consumer + +def default_consumer(msg): + """ the default consumer, prints the message to stdout (using 'print') """ + sys.stderr.write(str(msg)+"\n") + +default_keywordmapper = KeywordMapper() + +def setconsumer(keywords, consumer): + default_keywordmapper.setconsumer(keywords, consumer) + +def setstate(state): + default_keywordmapper.setstate(state) +def getstate(): + return default_keywordmapper.getstate() + +# +# Consumers +# + +class File(object): + """ log consumer wrapping a file(-like) object """ + def __init__(self, f): + assert hasattr(f, 'write') + #assert isinstance(f, file) or not hasattr(f, 'open') + self._file = f + + def __call__(self, msg): + """ write a message to the log """ + self._file.write(str(msg) + "\n") + if hasattr(self._file, 'flush'): + self._file.flush() + +class Path(object): + """ log consumer that opens and writes to a Path """ + def __init__(self, filename, append=False, + delayed_create=False, buffering=False): + self._append = append + self._filename = str(filename) + self._buffering = buffering + if not delayed_create: + self._openfile() + + def _openfile(self): + mode = self._append and 'a' or 'w' + f = open(self._filename, mode) + self._file = f + + def __call__(self, msg): + """ write a message to the log """ + if not hasattr(self, "_file"): + self._openfile() + self._file.write(str(msg) + "\n") + if not self._buffering: + self._file.flush() + +def STDOUT(msg): + """ consumer that writes to sys.stdout """ + sys.stdout.write(str(msg)+"\n") + +def STDERR(msg): + """ consumer that writes to sys.stderr """ + sys.stderr.write(str(msg)+"\n") + +class Syslog: + """ consumer that writes to the syslog daemon """ + + def __init__(self, priority = None): + if priority is None: + priority = self.LOG_INFO + self.priority = priority + + def __call__(self, msg): + """ write a message to the log """ + py.std.syslog.syslog(self.priority, str(msg)) + +for _prio in "EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG".split(): + _prio = "LOG_" + _prio + try: + setattr(Syslog, _prio, getattr(py.std.syslog, _prio)) + except AttributeError: + pass diff --git a/venv/lib/python3.5/site-packages/py/_log/warning.py b/venv/lib/python3.5/site-packages/py/_log/warning.py new file mode 100644 index 0000000..a137fe8 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_log/warning.py @@ -0,0 +1,76 @@ +import py, sys + +class DeprecationWarning(DeprecationWarning): + def __init__(self, msg, path, lineno): + self.msg = msg + self.path = path + self.lineno = lineno + def __repr__(self): + return "%s:%d: %s" %(self.path, self.lineno+1, self.msg) + def __str__(self): + return self.msg + +def _apiwarn(startversion, msg, stacklevel=2, function=None): + # below is mostly COPIED from python2.4/warnings.py's def warn() + # Get context information + if isinstance(stacklevel, str): + frame = sys._getframe(1) + level = 1 + found = frame.f_code.co_filename.find(stacklevel) != -1 + while frame: + co = frame.f_code + if co.co_filename.find(stacklevel) == -1: + if found: + stacklevel = level + break + else: + found = True + level += 1 + frame = frame.f_back + else: + stacklevel = 1 + msg = "%s (since version %s)" %(msg, startversion) + warn(msg, stacklevel=stacklevel+1, function=function) + +def warn(msg, stacklevel=1, function=None): + if function is not None: + filename = py.std.inspect.getfile(function) + lineno = py.code.getrawcode(function).co_firstlineno + else: + try: + caller = sys._getframe(stacklevel) + except ValueError: + globals = sys.__dict__ + lineno = 1 + else: + globals = caller.f_globals + lineno = caller.f_lineno + if '__name__' in globals: + module = globals['__name__'] + else: + module = "<string>" + filename = globals.get('__file__') + if filename: + fnl = filename.lower() + if fnl.endswith(".pyc") or fnl.endswith(".pyo"): + filename = filename[:-1] + elif fnl.endswith("$py.class"): + filename = filename.replace('$py.class', '.py') + else: + if module == "__main__": + try: + filename = sys.argv[0] + except AttributeError: + # embedded interpreters don't have sys.argv, see bug #839151 + filename = '__main__' + if not filename: + filename = module + path = py.path.local(filename) + warning = DeprecationWarning(msg, path, lineno) + py.std.warnings.warn_explicit(warning, category=Warning, + filename=str(warning.path), + lineno=warning.lineno, + registry=py.std.warnings.__dict__.setdefault( + "__warningsregistry__", {}) + ) + diff --git a/venv/lib/python3.5/site-packages/py/_path/__init__.py b/venv/lib/python3.5/site-packages/py/_path/__init__.py new file mode 100644 index 0000000..7b1ea8a --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_path/__init__.py @@ -0,0 +1 @@ +""" unified file system api """ diff --git a/venv/lib/python3.5/site-packages/py/_path/cacheutil.py b/venv/lib/python3.5/site-packages/py/_path/cacheutil.py new file mode 100644 index 0000000..89ea90b --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_path/cacheutil.py @@ -0,0 +1,114 @@ +""" +This module contains multithread-safe cache implementations. + +All Caches have + + getorbuild(key, builder) + delentry(key) + +methods and allow configuration when instantiating the cache class. +""" +from time import time as gettime + +class BasicCache(object): + def __init__(self, maxentries=128): + self.maxentries = maxentries + self.prunenum = int(maxentries - maxentries/8) + self._dict = {} + + def clear(self): + self._dict.clear() + + def _getentry(self, key): + return self._dict[key] + + def _putentry(self, key, entry): + self._prunelowestweight() + self._dict[key] = entry + + def delentry(self, key, raising=False): + try: + del self._dict[key] + except KeyError: + if raising: + raise + + def getorbuild(self, key, builder): + try: + entry = self._getentry(key) + except KeyError: + entry = self._build(key, builder) + self._putentry(key, entry) + return entry.value + + def _prunelowestweight(self): + """ prune out entries with lowest weight. """ + numentries = len(self._dict) + if numentries >= self.maxentries: + # evict according to entry's weight + items = [(entry.weight, key) + for key, entry in self._dict.items()] + items.sort() + index = numentries - self.prunenum + if index > 0: + for weight, key in items[:index]: + # in MT situations the element might be gone + self.delentry(key, raising=False) + +class BuildcostAccessCache(BasicCache): + """ A BuildTime/Access-counting cache implementation. + the weight of a value is computed as the product of + + num-accesses-of-a-value * time-to-build-the-value + + The values with the least such weights are evicted + if the cache maxentries threshold is superceded. + For implementation flexibility more than one object + might be evicted at a time. + """ + # time function to use for measuring build-times + + def _build(self, key, builder): + start = gettime() + val = builder() + end = gettime() + return WeightedCountingEntry(val, end-start) + + +class WeightedCountingEntry(object): + def __init__(self, value, oneweight): + self._value = value + self.weight = self._oneweight = oneweight + + def value(self): + self.weight += self._oneweight + return self._value + value = property(value) + +class AgingCache(BasicCache): + """ This cache prunes out cache entries that are too old. + """ + def __init__(self, maxentries=128, maxseconds=10.0): + super(AgingCache, self).__init__(maxentries) + self.maxseconds = maxseconds + + def _getentry(self, key): + entry = self._dict[key] + if entry.isexpired(): + self.delentry(key) + raise KeyError(key) + return entry + + def _build(self, key, builder): + val = builder() + entry = AgingEntry(val, gettime() + self.maxseconds) + return entry + +class AgingEntry(object): + def __init__(self, value, expirationtime): + self.value = value + self.weight = expirationtime + + def isexpired(self): + t = gettime() + return t >= self.weight diff --git a/venv/lib/python3.5/site-packages/py/_path/common.py b/venv/lib/python3.5/site-packages/py/_path/common.py new file mode 100644 index 0000000..4bc292c --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_path/common.py @@ -0,0 +1,445 @@ +""" +""" +import os, sys, posixpath +import fnmatch +import py + +# Moved from local.py. +iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt') + +try: + from os import fspath +except ImportError: + def fspath(path): + """ + Return the string representation of the path. + If str or bytes is passed in, it is returned unchanged. + This code comes from PEP 519, modified to support earlier versions of + python. + + This is required for python < 3.6. + """ + if isinstance(path, (py.builtin.text, py.builtin.bytes)): + return path + + # Work from the object's type to match method resolution of other magic + # methods. + path_type = type(path) + try: + return path_type.__fspath__(path) + except AttributeError: + if hasattr(path_type, '__fspath__'): + raise + try: + import pathlib + except ImportError: + pass + else: + if isinstance(path, pathlib.PurePath): + return py.builtin.text(path) + + raise TypeError("expected str, bytes or os.PathLike object, not " + + path_type.__name__) + +class Checkers: + _depend_on_existence = 'exists', 'link', 'dir', 'file' + + def __init__(self, path): + self.path = path + + def dir(self): + raise NotImplementedError + + def file(self): + raise NotImplementedError + + def dotfile(self): + return self.path.basename.startswith('.') + + def ext(self, arg): + if not arg.startswith('.'): + arg = '.' + arg + return self.path.ext == arg + + def exists(self): + raise NotImplementedError + + def basename(self, arg): + return self.path.basename == arg + + def basestarts(self, arg): + return self.path.basename.startswith(arg) + + def relto(self, arg): + return self.path.relto(arg) + + def fnmatch(self, arg): + return self.path.fnmatch(arg) + + def endswith(self, arg): + return str(self.path).endswith(arg) + + def _evaluate(self, kw): + for name, value in kw.items(): + invert = False + meth = None + try: + meth = getattr(self, name) + except AttributeError: + if name[:3] == 'not': + invert = True + try: + meth = getattr(self, name[3:]) + except AttributeError: + pass + if meth is None: + raise TypeError( + "no %r checker available for %r" % (name, self.path)) + try: + if py.code.getrawcode(meth).co_argcount > 1: + if (not meth(value)) ^ invert: + return False + else: + if bool(value) ^ bool(meth()) ^ invert: + return False + except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY): + # EBUSY feels not entirely correct, + # but its kind of necessary since ENOMEDIUM + # is not accessible in python + for name in self._depend_on_existence: + if name in kw: + if kw.get(name): + return False + name = 'not' + name + if name in kw: + if not kw.get(name): + return False + return True + +class NeverRaised(Exception): + pass + +class PathBase(object): + """ shared implementation for filesystem path objects.""" + Checkers = Checkers + + def __div__(self, other): + return self.join(fspath(other)) + __truediv__ = __div__ # py3k + + def basename(self): + """ basename part of path. """ + return self._getbyspec('basename')[0] + basename = property(basename, None, None, basename.__doc__) + + def dirname(self): + """ dirname part of path. """ + return self._getbyspec('dirname')[0] + dirname = property(dirname, None, None, dirname.__doc__) + + def purebasename(self): + """ pure base name of the path.""" + return self._getbyspec('purebasename')[0] + purebasename = property(purebasename, None, None, purebasename.__doc__) + + def ext(self): + """ extension of the path (including the '.').""" + return self._getbyspec('ext')[0] + ext = property(ext, None, None, ext.__doc__) + + def dirpath(self, *args, **kwargs): + """ return the directory path joined with any given path arguments. """ + return self.new(basename='').join(*args, **kwargs) + + def read_binary(self): + """ read and return a bytestring from reading the path. """ + with self.open('rb') as f: + return f.read() + + def read_text(self, encoding): + """ read and return a Unicode string from reading the path. """ + with self.open("r", encoding=encoding) as f: + return f.read() + + + def read(self, mode='r'): + """ read and return a bytestring from reading the path. """ + with self.open(mode) as f: + return f.read() + + def readlines(self, cr=1): + """ read and return a list of lines from the path. if cr is False, the +newline will be removed from the end of each line. """ + if sys.version_info < (3, ): + mode = 'rU' + else: # python 3 deprecates mode "U" in favor of "newline" option + mode = 'r' + + if not cr: + content = self.read(mode) + return content.split('\n') + else: + f = self.open(mode) + try: + return f.readlines() + finally: + f.close() + + def load(self): + """ (deprecated) return object unpickled from self.read() """ + f = self.open('rb') + try: + return py.error.checked_call(py.std.pickle.load, f) + finally: + f.close() + + def move(self, target): + """ move this path to target. """ + if target.relto(self): + raise py.error.EINVAL(target, + "cannot move path into a subdirectory of itself") + try: + self.rename(target) + except py.error.EXDEV: # invalid cross-device link + self.copy(target) + self.remove() + + def __repr__(self): + """ return a string representation of this path. """ + return repr(str(self)) + + def check(self, **kw): + """ check a path for existence and properties. + + Without arguments, return True if the path exists, otherwise False. + + valid checkers:: + + file=1 # is a file + file=0 # is not a file (may not even exist) + dir=1 # is a dir + link=1 # is a link + exists=1 # exists + + You can specify multiple checker definitions, for example:: + + path.check(file=1, link=1) # a link pointing to a file + """ + if not kw: + kw = {'exists' : 1} + return self.Checkers(self)._evaluate(kw) + + def fnmatch(self, pattern): + """return true if the basename/fullname matches the glob-'pattern'. + + valid pattern characters:: + + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq + + If the pattern contains a path-separator then the full path + is used for pattern matching and a '*' is prepended to the + pattern. + + if the pattern doesn't contain a path-separator the pattern + is only matched against the basename. + """ + return FNMatcher(pattern)(self) + + def relto(self, relpath): + """ return a string which is the relative part of the path + to the given 'relpath'. + """ + if not isinstance(relpath, (str, PathBase)): + raise TypeError("%r: not a string or path object" %(relpath,)) + strrelpath = str(relpath) + if strrelpath and strrelpath[-1] != self.sep: + strrelpath += self.sep + #assert strrelpath[-1] == self.sep + #assert strrelpath[-2] != self.sep + strself = self.strpath + if sys.platform == "win32" or getattr(os, '_name', None) == 'nt': + if os.path.normcase(strself).startswith( + os.path.normcase(strrelpath)): + return strself[len(strrelpath):] + elif strself.startswith(strrelpath): + return strself[len(strrelpath):] + return "" + + def ensure_dir(self, *args): + """ ensure the path joined with args is a directory. """ + return self.ensure(*args, **{"dir": True}) + + def bestrelpath(self, dest): + """ return a string which is a relative path from self + (assumed to be a directory) to dest such that + self.join(bestrelpath) == dest and if not such + path can be determined return dest. + """ + try: + if self == dest: + return os.curdir + base = self.common(dest) + if not base: # can be the case on windows + return str(dest) + self2base = self.relto(base) + reldest = dest.relto(base) + if self2base: + n = self2base.count(self.sep) + 1 + else: + n = 0 + l = [os.pardir] * n + if reldest: + l.append(reldest) + target = dest.sep.join(l) + return target + except AttributeError: + return str(dest) + + def exists(self): + return self.check() + + def isdir(self): + return self.check(dir=1) + + def isfile(self): + return self.check(file=1) + + def parts(self, reverse=False): + """ return a root-first list of all ancestor directories + plus the path itself. + """ + current = self + l = [self] + while 1: + last = current + current = current.dirpath() + if last == current: + break + l.append(current) + if not reverse: + l.reverse() + return l + + def common(self, other): + """ return the common part shared with the other path + or None if there is no common part. + """ + last = None + for x, y in zip(self.parts(), other.parts()): + if x != y: + return last + last = x + return last + + def __add__(self, other): + """ return new path object with 'other' added to the basename""" + return self.new(basename=self.basename+str(other)) + + def __cmp__(self, other): + """ return sort value (-1, 0, +1). """ + try: + return cmp(self.strpath, other.strpath) + except AttributeError: + return cmp(str(self), str(other)) # self.path, other.path) + + def __lt__(self, other): + try: + return self.strpath < other.strpath + except AttributeError: + return str(self) < str(other) + + def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False): + """ yields all paths below the current one + + fil is a filter (glob pattern or callable), if not matching the + path will not be yielded, defaulting to None (everything is + returned) + + rec is a filter (glob pattern or callable) that controls whether + a node is descended, defaulting to None + + ignore is an Exception class that is ignoredwhen calling dirlist() + on any of the paths (by default, all exceptions are reported) + + bf if True will cause a breadthfirst search instead of the + default depthfirst. Default: False + + sort if True will sort entries within each directory level. + """ + for x in Visitor(fil, rec, ignore, bf, sort).gen(self): + yield x + + def _sortlist(self, res, sort): + if sort: + if hasattr(sort, '__call__'): + res.sort(sort) + else: + res.sort() + + def samefile(self, other): + """ return True if other refers to the same stat object as self. """ + return self.strpath == str(other) + + def __fspath__(self): + return self.strpath + +class Visitor: + def __init__(self, fil, rec, ignore, bf, sort): + if isinstance(fil, py.builtin._basestring): + fil = FNMatcher(fil) + if isinstance(rec, py.builtin._basestring): + self.rec = FNMatcher(rec) + elif not hasattr(rec, '__call__') and rec: + self.rec = lambda path: True + else: + self.rec = rec + self.fil = fil + self.ignore = ignore + self.breadthfirst = bf + self.optsort = sort and sorted or (lambda x: x) + + def gen(self, path): + try: + entries = path.listdir() + except self.ignore: + return + rec = self.rec + dirs = self.optsort([p for p in entries + if p.check(dir=1) and (rec is None or rec(p))]) + if not self.breadthfirst: + for subdir in dirs: + for p in self.gen(subdir): + yield p + for p in self.optsort(entries): + if self.fil is None or self.fil(p): + yield p + if self.breadthfirst: + for subdir in dirs: + for p in self.gen(subdir): + yield p + +class FNMatcher: + def __init__(self, pattern): + self.pattern = pattern + + def __call__(self, path): + pattern = self.pattern + + if (pattern.find(path.sep) == -1 and + iswin32 and + pattern.find(posixpath.sep) != -1): + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posixpath.sep, path.sep) + + if pattern.find(path.sep) == -1: + name = path.basename + else: + name = str(path) # path.strpath # XXX svn? + if not os.path.isabs(pattern): + pattern = '*' + path.sep + pattern + return fnmatch.fnmatch(name, pattern) diff --git a/venv/lib/python3.5/site-packages/py/_path/local.py b/venv/lib/python3.5/site-packages/py/_path/local.py new file mode 100644 index 0000000..264f115 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_path/local.py @@ -0,0 +1,930 @@ +""" +local path implementation. +""" +from __future__ import with_statement + +from contextlib import contextmanager +import sys, os, re, atexit, io +import py +from py._path import common +from py._path.common import iswin32, fspath +from stat import S_ISLNK, S_ISDIR, S_ISREG + +from os.path import abspath, normcase, normpath, isabs, exists, isdir, isfile, islink, dirname + +if sys.version_info > (3,0): + def map_as_list(func, iter): + return list(map(func, iter)) +else: + map_as_list = map + +class Stat(object): + def __getattr__(self, name): + return getattr(self._osstatresult, "st_" + name) + + def __init__(self, path, osstatresult): + self.path = path + self._osstatresult = osstatresult + + @property + def owner(self): + if iswin32: + raise NotImplementedError("XXX win32") + import pwd + entry = py.error.checked_call(pwd.getpwuid, self.uid) + return entry[0] + + @property + def group(self): + """ return group name of file. """ + if iswin32: + raise NotImplementedError("XXX win32") + import grp + entry = py.error.checked_call(grp.getgrgid, self.gid) + return entry[0] + + def isdir(self): + return S_ISDIR(self._osstatresult.st_mode) + + def isfile(self): + return S_ISREG(self._osstatresult.st_mode) + + def islink(self): + st = self.path.lstat() + return S_ISLNK(self._osstatresult.st_mode) + +class PosixPath(common.PathBase): + def chown(self, user, group, rec=0): + """ change ownership to the given user and group. + user and group may be specified by a number or + by a name. if rec is True change ownership + recursively. + """ + uid = getuserid(user) + gid = getgroupid(group) + if rec: + for x in self.visit(rec=lambda x: x.check(link=0)): + if x.check(link=0): + py.error.checked_call(os.chown, str(x), uid, gid) + py.error.checked_call(os.chown, str(self), uid, gid) + + def readlink(self): + """ return value of a symbolic link. """ + return py.error.checked_call(os.readlink, self.strpath) + + def mklinkto(self, oldname): + """ posix style hard link to another name. """ + py.error.checked_call(os.link, str(oldname), str(self)) + + def mksymlinkto(self, value, absolute=1): + """ create a symbolic link with the given value (pointing to another name). """ + if absolute: + py.error.checked_call(os.symlink, str(value), self.strpath) + else: + base = self.common(value) + # with posix local paths '/' is always a common base + relsource = self.__class__(value).relto(base) + reldest = self.relto(base) + n = reldest.count(self.sep) + target = self.sep.join(('..', )*n + (relsource, )) + py.error.checked_call(os.symlink, target, self.strpath) + +def getuserid(user): + import pwd + if not isinstance(user, int): + user = pwd.getpwnam(user)[2] + return user + +def getgroupid(group): + import grp + if not isinstance(group, int): + group = grp.getgrnam(group)[2] + return group + +FSBase = not iswin32 and PosixPath or common.PathBase + +class LocalPath(FSBase): + """ object oriented interface to os.path and other local filesystem + related information. + """ + class ImportMismatchError(ImportError): + """ raised on pyimport() if there is a mismatch of __file__'s""" + + sep = os.sep + class Checkers(common.Checkers): + def _stat(self): + try: + return self._statcache + except AttributeError: + try: + self._statcache = self.path.stat() + except py.error.ELOOP: + self._statcache = self.path.lstat() + return self._statcache + + def dir(self): + return S_ISDIR(self._stat().mode) + + def file(self): + return S_ISREG(self._stat().mode) + + def exists(self): + return self._stat() + + def link(self): + st = self.path.lstat() + return S_ISLNK(st.mode) + + def __init__(self, path=None, expanduser=False): + """ Initialize and return a local Path instance. + + Path can be relative to the current directory. + If path is None it defaults to the current working directory. + If expanduser is True, tilde-expansion is performed. + Note that Path instances always carry an absolute path. + Note also that passing in a local path object will simply return + the exact same path object. Use new() to get a new copy. + """ + if path is None: + self.strpath = py.error.checked_call(os.getcwd) + else: + try: + path = fspath(path) + except TypeError: + raise ValueError("can only pass None, Path instances " + "or non-empty strings to LocalPath") + if expanduser: + path = os.path.expanduser(path) + self.strpath = abspath(path) + + def __hash__(self): + return hash(self.strpath) + + def __eq__(self, other): + s1 = fspath(self) + try: + s2 = fspath(other) + except TypeError: + return False + if iswin32: + s1 = s1.lower() + try: + s2 = s2.lower() + except AttributeError: + return False + return s1 == s2 + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return fspath(self) < fspath(other) + + def __gt__(self, other): + return fspath(self) > fspath(other) + + def samefile(self, other): + """ return True if 'other' references the same file as 'self'. + """ + other = fspath(other) + if not isabs(other): + other = abspath(other) + if self == other: + return True + if iswin32: + return False # there is no samefile + return py.error.checked_call( + os.path.samefile, self.strpath, other) + + def remove(self, rec=1, ignore_errors=False): + """ remove a file or directory (or a directory tree if rec=1). + if ignore_errors is True, errors while removing directories will + be ignored. + """ + if self.check(dir=1, link=0): + if rec: + # force remove of readonly files on windows + if iswin32: + self.chmod(0o700, rec=1) + py.error.checked_call(py.std.shutil.rmtree, self.strpath, + ignore_errors=ignore_errors) + else: + py.error.checked_call(os.rmdir, self.strpath) + else: + if iswin32: + self.chmod(0o700) + py.error.checked_call(os.remove, self.strpath) + + def computehash(self, hashtype="md5", chunksize=524288): + """ return hexdigest of hashvalue for this file. """ + try: + try: + import hashlib as mod + except ImportError: + if hashtype == "sha1": + hashtype = "sha" + mod = __import__(hashtype) + hash = getattr(mod, hashtype)() + except (AttributeError, ImportError): + raise ValueError("Don't know how to compute %r hash" %(hashtype,)) + f = self.open('rb') + try: + while 1: + buf = f.read(chunksize) + if not buf: + return hash.hexdigest() + hash.update(buf) + finally: + f.close() + + def new(self, **kw): + """ create a modified version of this path. + the following keyword arguments modify various path parts:: + + a:/some/path/to/a/file.ext + xx drive + xxxxxxxxxxxxxxxxx dirname + xxxxxxxx basename + xxxx purebasename + xxx ext + """ + obj = object.__new__(self.__class__) + if not kw: + obj.strpath = self.strpath + return obj + drive, dirname, basename, purebasename,ext = self._getbyspec( + "drive,dirname,basename,purebasename,ext") + if 'basename' in kw: + if 'purebasename' in kw or 'ext' in kw: + raise ValueError("invalid specification %r" % kw) + else: + pb = kw.setdefault('purebasename', purebasename) + try: + ext = kw['ext'] + except KeyError: + pass + else: + if ext and not ext.startswith('.'): + ext = '.' + ext + kw['basename'] = pb + ext + + if ('dirname' in kw and not kw['dirname']): + kw['dirname'] = drive + else: + kw.setdefault('dirname', dirname) + kw.setdefault('sep', self.sep) + obj.strpath = normpath( + "%(dirname)s%(sep)s%(basename)s" % kw) + return obj + + def _getbyspec(self, spec): + """ see new for what 'spec' can be. """ + res = [] + parts = self.strpath.split(self.sep) + + args = filter(None, spec.split(',') ) + append = res.append + for name in args: + if name == 'drive': + append(parts[0]) + elif name == 'dirname': + append(self.sep.join(parts[:-1])) + else: + basename = parts[-1] + if name == 'basename': + append(basename) + else: + i = basename.rfind('.') + if i == -1: + purebasename, ext = basename, '' + else: + purebasename, ext = basename[:i], basename[i:] + if name == 'purebasename': + append(purebasename) + elif name == 'ext': + append(ext) + else: + raise ValueError("invalid part specification %r" % name) + return res + + def dirpath(self, *args, **kwargs): + """ return the directory path joined with any given path arguments. """ + if not kwargs: + path = object.__new__(self.__class__) + path.strpath = dirname(self.strpath) + if args: + path = path.join(*args) + return path + return super(LocalPath, self).dirpath(*args, **kwargs) + + def join(self, *args, **kwargs): + """ return a new path by appending all 'args' as path + components. if abs=1 is used restart from root if any + of the args is an absolute path. + """ + sep = self.sep + strargs = [fspath(arg) for arg in args] + strpath = self.strpath + if kwargs.get('abs'): + newargs = [] + for arg in reversed(strargs): + if isabs(arg): + strpath = arg + strargs = newargs + break + newargs.insert(0, arg) + for arg in strargs: + arg = arg.strip(sep) + if iswin32: + # allow unix style paths even on windows. + arg = arg.strip('/') + arg = arg.replace('/', sep) + strpath = strpath + sep + arg + obj = object.__new__(self.__class__) + obj.strpath = normpath(strpath) + return obj + + def open(self, mode='r', ensure=False, encoding=None): + """ return an opened file with the given mode. + + If ensure is True, create parent directories if needed. + """ + if ensure: + self.dirpath().ensure(dir=1) + if encoding: + return py.error.checked_call(io.open, self.strpath, mode, encoding=encoding) + return py.error.checked_call(open, self.strpath, mode) + + def _fastjoin(self, name): + child = object.__new__(self.__class__) + child.strpath = self.strpath + self.sep + name + return child + + def islink(self): + return islink(self.strpath) + + def check(self, **kw): + if not kw: + return exists(self.strpath) + if len(kw) == 1: + if "dir" in kw: + return not kw["dir"] ^ isdir(self.strpath) + if "file" in kw: + return not kw["file"] ^ isfile(self.strpath) + return super(LocalPath, self).check(**kw) + + _patternchars = set("*?[" + os.path.sep) + def listdir(self, fil=None, sort=None): + """ list directory contents, possibly filter by the given fil func + and possibly sorted. + """ + if fil is None and sort is None: + names = py.error.checked_call(os.listdir, self.strpath) + return map_as_list(self._fastjoin, names) + if isinstance(fil, py.builtin._basestring): + if not self._patternchars.intersection(fil): + child = self._fastjoin(fil) + if exists(child.strpath): + return [child] + return [] + fil = common.FNMatcher(fil) + names = py.error.checked_call(os.listdir, self.strpath) + res = [] + for name in names: + child = self._fastjoin(name) + if fil is None or fil(child): + res.append(child) + self._sortlist(res, sort) + return res + + def size(self): + """ return size of the underlying file object """ + return self.stat().size + + def mtime(self): + """ return last modification time of the path. """ + return self.stat().mtime + + def copy(self, target, mode=False, stat=False): + """ copy path to target. + + If mode is True, will copy copy permission from path to target. + If stat is True, copy permission, last modification + time, last access time, and flags from path to target. + """ + if self.check(file=1): + if target.check(dir=1): + target = target.join(self.basename) + assert self!=target + copychunked(self, target) + if mode: + copymode(self.strpath, target.strpath) + if stat: + copystat(self, target) + else: + def rec(p): + return p.check(link=0) + for x in self.visit(rec=rec): + relpath = x.relto(self) + newx = target.join(relpath) + newx.dirpath().ensure(dir=1) + if x.check(link=1): + newx.mksymlinkto(x.readlink()) + continue + elif x.check(file=1): + copychunked(x, newx) + elif x.check(dir=1): + newx.ensure(dir=1) + if mode: + copymode(x.strpath, newx.strpath) + if stat: + copystat(x, newx) + + def rename(self, target): + """ rename this path to target. """ + target = fspath(target) + return py.error.checked_call(os.rename, self.strpath, target) + + def dump(self, obj, bin=1): + """ pickle object into path location""" + f = self.open('wb') + try: + py.error.checked_call(py.std.pickle.dump, obj, f, bin) + finally: + f.close() + + def mkdir(self, *args): + """ create & return the directory joined with args. """ + p = self.join(*args) + py.error.checked_call(os.mkdir, fspath(p)) + return p + + def write_binary(self, data, ensure=False): + """ write binary data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open('wb') as f: + f.write(data) + + def write_text(self, data, encoding, ensure=False): + """ write text data into path using the specified encoding. + If ensure is True create missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open('w', encoding=encoding) as f: + f.write(data) + + def write(self, data, mode='w', ensure=False): + """ write data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + if 'b' in mode: + if not py.builtin._isbytes(data): + raise ValueError("can only process bytes") + else: + if not py.builtin._istext(data): + if not py.builtin._isbytes(data): + data = str(data) + else: + data = py.builtin._totext(data, sys.getdefaultencoding()) + f = self.open(mode) + try: + f.write(data) + finally: + f.close() + + def _ensuredirs(self): + parent = self.dirpath() + if parent == self: + return self + if parent.check(dir=0): + parent._ensuredirs() + if self.check(dir=0): + try: + self.mkdir() + except py.error.EEXIST: + # race condition: file/dir created by another thread/process. + # complain if it is not a dir + if self.check(dir=0): + raise + return self + + def ensure(self, *args, **kwargs): + """ ensure that an args-joined path exists (by default as + a file). if you specify a keyword argument 'dir=True' + then the path is forced to be a directory path. + """ + p = self.join(*args) + if kwargs.get('dir', 0): + return p._ensuredirs() + else: + p.dirpath()._ensuredirs() + if not p.check(file=1): + p.open('w').close() + return p + + def stat(self, raising=True): + """ Return an os.stat() tuple. """ + if raising == True: + return Stat(self, py.error.checked_call(os.stat, self.strpath)) + try: + return Stat(self, os.stat(self.strpath)) + except KeyboardInterrupt: + raise + except Exception: + return None + + def lstat(self): + """ Return an os.lstat() tuple. """ + return Stat(self, py.error.checked_call(os.lstat, self.strpath)) + + def setmtime(self, mtime=None): + """ set modification time for the given path. if 'mtime' is None + (the default) then the file's mtime is set to current time. + + Note that the resolution for 'mtime' is platform dependent. + """ + if mtime is None: + return py.error.checked_call(os.utime, self.strpath, mtime) + try: + return py.error.checked_call(os.utime, self.strpath, (-1, mtime)) + except py.error.EINVAL: + return py.error.checked_call(os.utime, self.strpath, (self.atime(), mtime)) + + def chdir(self): + """ change directory to self and return old current directory """ + try: + old = self.__class__() + except py.error.ENOENT: + old = None + py.error.checked_call(os.chdir, self.strpath) + return old + + + @contextmanager + def as_cwd(self): + """ return context manager which changes to current dir during the + managed "with" context. On __enter__ it returns the old dir. + """ + old = self.chdir() + try: + yield old + finally: + old.chdir() + + def realpath(self): + """ return a new path which contains no symbolic links.""" + return self.__class__(os.path.realpath(self.strpath)) + + def atime(self): + """ return last access time of the path. """ + return self.stat().atime + + def __repr__(self): + return 'local(%r)' % self.strpath + + def __str__(self): + """ return string representation of the Path. """ + return self.strpath + + def chmod(self, mode, rec=0): + """ change permissions to the given mode. If mode is an + integer it directly encodes the os-specific modes. + if rec is True perform recursively. + """ + if not isinstance(mode, int): + raise TypeError("mode %r must be an integer" % (mode,)) + if rec: + for x in self.visit(rec=rec): + py.error.checked_call(os.chmod, str(x), mode) + py.error.checked_call(os.chmod, self.strpath, mode) + + def pypkgpath(self): + """ return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + Return None if a pkgpath can not be determined. + """ + pkgpath = None + for parent in self.parts(reverse=True): + if parent.isdir(): + if not parent.join('__init__.py').exists(): + break + if not isimportable(parent.basename): + break + pkgpath = parent + return pkgpath + + def _ensuresyspath(self, ensuremode, path): + if ensuremode: + s = str(path) + if ensuremode == "append": + if s not in sys.path: + sys.path.append(s) + else: + if s != sys.path[0]: + sys.path.insert(0, s) + + def pyimport(self, modname=None, ensuresyspath=True): + """ return path as an imported python module. + + If modname is None, look for the containing package + and construct an according module name. + The module will be put/looked up in sys.modules. + if ensuresyspath is True then the root dir for importing + the file (taking __init__.py files into account) will + be prepended to sys.path if it isn't there already. + If ensuresyspath=="append" the root dir will be appended + if it isn't already contained in sys.path. + if ensuresyspath is False no modification of syspath happens. + """ + if not self.check(): + raise py.error.ENOENT(self) + + pkgpath = None + if modname is None: + pkgpath = self.pypkgpath() + if pkgpath is not None: + pkgroot = pkgpath.dirpath() + names = self.new(ext="").relto(pkgroot).split(self.sep) + if names[-1] == "__init__": + names.pop() + modname = ".".join(names) + else: + pkgroot = self.dirpath() + modname = self.purebasename + + self._ensuresyspath(ensuresyspath, pkgroot) + __import__(modname) + mod = sys.modules[modname] + if self.basename == "__init__.py": + return mod # we don't check anything as we might + # we in a namespace package ... too icky to check + modfile = mod.__file__ + if modfile[-4:] in ('.pyc', '.pyo'): + modfile = modfile[:-1] + elif modfile.endswith('$py.class'): + modfile = modfile[:-9] + '.py' + if modfile.endswith(os.path.sep + "__init__.py"): + if self.basename != "__init__.py": + modfile = modfile[:-12] + try: + issame = self.samefile(modfile) + except py.error.ENOENT: + issame = False + if not issame: + raise self.ImportMismatchError(modname, modfile, self) + return mod + else: + try: + return sys.modules[modname] + except KeyError: + # we have a custom modname, do a pseudo-import + mod = py.std.types.ModuleType(modname) + mod.__file__ = str(self) + sys.modules[modname] = mod + try: + py.builtin.execfile(str(self), mod.__dict__) + except: + del sys.modules[modname] + raise + return mod + + def sysexec(self, *argv, **popen_opts): + """ return stdout text from executing a system child process, + where the 'self' path points to executable. + The process is directly invoked and not through a system shell. + """ + from subprocess import Popen, PIPE + argv = map_as_list(str, argv) + popen_opts['stdout'] = popen_opts['stderr'] = PIPE + proc = Popen([str(self)] + argv, **popen_opts) + stdout, stderr = proc.communicate() + ret = proc.wait() + if py.builtin._isbytes(stdout): + stdout = py.builtin._totext(stdout, sys.getdefaultencoding()) + if ret != 0: + if py.builtin._isbytes(stderr): + stderr = py.builtin._totext(stderr, sys.getdefaultencoding()) + raise py.process.cmdexec.Error(ret, ret, str(self), + stdout, stderr,) + return stdout + + def sysfind(cls, name, checker=None, paths=None): + """ return a path object found by looking at the systems + underlying PATH specification. If the checker is not None + it will be invoked to filter matching paths. If a binary + cannot be found, None is returned + Note: This is probably not working on plain win32 systems + but may work on cygwin. + """ + if isabs(name): + p = py.path.local(name) + if p.check(file=1): + return p + else: + if paths is None: + if iswin32: + paths = py.std.os.environ['Path'].split(';') + if '' not in paths and '.' not in paths: + paths.append('.') + try: + systemroot = os.environ['SYSTEMROOT'] + except KeyError: + pass + else: + paths = [re.sub('%SystemRoot%', systemroot, path) + for path in paths] + else: + paths = py.std.os.environ['PATH'].split(':') + tryadd = [] + if iswin32: + tryadd += os.environ['PATHEXT'].split(os.pathsep) + tryadd.append("") + + for x in paths: + for addext in tryadd: + p = py.path.local(x).join(name, abs=True) + addext + try: + if p.check(file=1): + if checker: + if not checker(p): + continue + return p + except py.error.EACCES: + pass + return None + sysfind = classmethod(sysfind) + + def _gethomedir(cls): + try: + x = os.environ['HOME'] + except KeyError: + try: + x = os.environ["HOMEDRIVE"] + os.environ['HOMEPATH'] + except KeyError: + return None + return cls(x) + _gethomedir = classmethod(_gethomedir) + + #""" + #special class constructors for local filesystem paths + #""" + def get_temproot(cls): + """ return the system's temporary directory + (where tempfiles are usually created in) + """ + return py.path.local(py.std.tempfile.gettempdir()) + get_temproot = classmethod(get_temproot) + + def mkdtemp(cls, rootdir=None): + """ return a Path object pointing to a fresh new temporary directory + (which we created ourself). + """ + import tempfile + if rootdir is None: + rootdir = cls.get_temproot() + return cls(py.error.checked_call(tempfile.mkdtemp, dir=str(rootdir))) + mkdtemp = classmethod(mkdtemp) + + def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3, + lock_timeout = 172800): # two days + """ return unique directory with a number greater than the current + maximum one. The number is assumed to start directly after prefix. + if keep is true directories with a number less than (maxnum-keep) + will be removed. + """ + if rootdir is None: + rootdir = cls.get_temproot() + + nprefix = normcase(prefix) + def parse_num(path): + """ parse the number out of a path (if it matches the prefix) """ + nbasename = normcase(path.basename) + if nbasename.startswith(nprefix): + try: + return int(nbasename[len(nprefix):]) + except ValueError: + pass + + # compute the maximum number currently in use with the + # prefix + lastmax = None + while True: + maxnum = -1 + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None: + maxnum = max(maxnum, num) + + # make the new directory + try: + udir = rootdir.mkdir(prefix + str(maxnum+1)) + except py.error.EEXIST: + # race condition: another thread/process created the dir + # in the meantime. Try counting again + if lastmax == maxnum: + raise + lastmax = maxnum + continue + break + + # put a .lock file in the new directory that will be removed at + # process exit + if lock_timeout: + lockfile = udir.join('.lock') + mypid = os.getpid() + if hasattr(lockfile, 'mksymlinkto'): + lockfile.mksymlinkto(str(mypid)) + else: + lockfile.write(str(mypid)) + def try_remove_lockfile(): + # in a fork() situation, only the last process should + # remove the .lock, otherwise the other processes run the + # risk of seeing their temporary dir disappear. For now + # we remove the .lock in the parent only (i.e. we assume + # that the children finish before the parent). + if os.getpid() != mypid: + return + try: + lockfile.remove() + except py.error.Error: + pass + atexit.register(try_remove_lockfile) + + # prune old directories + if keep: + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None and num <= (maxnum - keep): + lf = path.join('.lock') + try: + t1 = lf.lstat().mtime + t2 = lockfile.lstat().mtime + if not lock_timeout or abs(t2-t1) < lock_timeout: + continue # skip directories still locked + except py.error.Error: + pass # assume that it means that there is no 'lf' + try: + path.remove(rec=1) + except KeyboardInterrupt: + raise + except: # this might be py.error.Error, WindowsError ... + pass + + # make link... + try: + username = os.environ['USER'] #linux, et al + except KeyError: + try: + username = os.environ['USERNAME'] #windows + except KeyError: + username = 'current' + + src = str(udir) + dest = src[:src.rfind('-')] + '-' + username + try: + os.unlink(dest) + except OSError: + pass + try: + os.symlink(src, dest) + except (OSError, AttributeError, NotImplementedError): + pass + + return udir + make_numbered_dir = classmethod(make_numbered_dir) + + +def copymode(src, dest): + """ copy permission from src to dst. """ + py.std.shutil.copymode(src, dest) + +def copystat(src, dest): + """ copy permission, last modification time, last access time, and flags from src to dst.""" + py.std.shutil.copystat(str(src), str(dest)) + +def copychunked(src, dest): + chunksize = 524288 # half a meg of bytes + fsrc = src.open('rb') + try: + fdest = dest.open('wb') + try: + while 1: + buf = fsrc.read(chunksize) + if not buf: + break + fdest.write(buf) + finally: + fdest.close() + finally: + fsrc.close() + +def isimportable(name): + if name and (name[0].isalpha() or name[0] == '_'): + name = name.replace("_", '') + return not name or name.isalnum() diff --git a/venv/lib/python3.5/site-packages/py/_path/svnurl.py b/venv/lib/python3.5/site-packages/py/_path/svnurl.py new file mode 100644 index 0000000..5f45fa2 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_path/svnurl.py @@ -0,0 +1,380 @@ +""" +module defining a subversion path object based on the external +command 'svn'. This modules aims to work with svn 1.3 and higher +but might also interact well with earlier versions. +""" + +import os, sys, time, re +import py +from py import path, process +from py._path import common +from py._path import svnwc as svncommon +from py._path.cacheutil import BuildcostAccessCache, AgingCache + +DEBUG=False + +class SvnCommandPath(svncommon.SvnPathBase): + """ path implementation that offers access to (possibly remote) subversion + repositories. """ + + _lsrevcache = BuildcostAccessCache(maxentries=128) + _lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0) + + def __new__(cls, path, rev=None, auth=None): + self = object.__new__(cls) + if isinstance(path, cls): + rev = path.rev + auth = path.auth + path = path.strpath + svncommon.checkbadchars(path) + path = path.rstrip('/') + self.strpath = path + self.rev = rev + self.auth = auth + return self + + def __repr__(self): + if self.rev == -1: + return 'svnurl(%r)' % self.strpath + else: + return 'svnurl(%r, %r)' % (self.strpath, self.rev) + + def _svnwithrev(self, cmd, *args): + """ execute an svn command, append our own url and revision """ + if self.rev is None: + return self._svnwrite(cmd, *args) + else: + args = ['-r', self.rev] + list(args) + return self._svnwrite(cmd, *args) + + def _svnwrite(self, cmd, *args): + """ execute an svn command, append our own url """ + l = ['svn %s' % cmd] + args = ['"%s"' % self._escape(item) for item in args] + l.extend(args) + l.append('"%s"' % self._encodedurl()) + # fixing the locale because we can't otherwise parse + string = " ".join(l) + if DEBUG: + print("execing %s" % string) + out = self._svncmdexecauth(string) + return out + + def _svncmdexecauth(self, cmd): + """ execute an svn command 'as is' """ + cmd = svncommon.fixlocale() + cmd + if self.auth is not None: + cmd += ' ' + self.auth.makecmdoptions() + return self._cmdexec(cmd) + + def _cmdexec(self, cmd): + try: + out = process.cmdexec(cmd) + except py.process.cmdexec.Error: + e = sys.exc_info()[1] + if (e.err.find('File Exists') != -1 or + e.err.find('File already exists') != -1): + raise py.error.EEXIST(self) + raise + return out + + def _svnpopenauth(self, cmd): + """ execute an svn command, return a pipe for reading stdin """ + cmd = svncommon.fixlocale() + cmd + if self.auth is not None: + cmd += ' ' + self.auth.makecmdoptions() + return self._popen(cmd) + + def _popen(self, cmd): + return os.popen(cmd) + + def _encodedurl(self): + return self._escape(self.strpath) + + def _norev_delentry(self, path): + auth = self.auth and self.auth.makecmdoptions() or None + self._lsnorevcache.delentry((str(path), auth)) + + def open(self, mode='r'): + """ return an opened file with the given mode. """ + if mode not in ("r", "rU",): + raise ValueError("mode %r not supported" % (mode,)) + assert self.check(file=1) # svn cat returns an empty file otherwise + if self.rev is None: + return self._svnpopenauth('svn cat "%s"' % ( + self._escape(self.strpath), )) + else: + return self._svnpopenauth('svn cat -r %s "%s"' % ( + self.rev, self._escape(self.strpath))) + + def dirpath(self, *args, **kwargs): + """ return the directory path of the current path joined + with any given path arguments. + """ + l = self.strpath.split(self.sep) + if len(l) < 4: + raise py.error.EINVAL(self, "base is not valid") + elif len(l) == 4: + return self.join(*args, **kwargs) + else: + return self.new(basename='').join(*args, **kwargs) + + # modifying methods (cache must be invalidated) + def mkdir(self, *args, **kwargs): + """ create & return the directory joined with args. + pass a 'msg' keyword argument to set the commit message. + """ + commit_msg = kwargs.get('msg', "mkdir by py lib invocation") + createpath = self.join(*args) + createpath._svnwrite('mkdir', '-m', commit_msg) + self._norev_delentry(createpath.dirpath()) + return createpath + + def copy(self, target, msg='copied by py lib invocation'): + """ copy path to target with checkin message msg.""" + if getattr(target, 'rev', None) is not None: + raise py.error.EINVAL(target, "revisions are immutable") + self._svncmdexecauth('svn copy -m "%s" "%s" "%s"' %(msg, + self._escape(self), self._escape(target))) + self._norev_delentry(target.dirpath()) + + def rename(self, target, msg="renamed by py lib invocation"): + """ rename this path to target with checkin message msg. """ + if getattr(self, 'rev', None) is not None: + raise py.error.EINVAL(self, "revisions are immutable") + self._svncmdexecauth('svn move -m "%s" --force "%s" "%s"' %( + msg, self._escape(self), self._escape(target))) + self._norev_delentry(self.dirpath()) + self._norev_delentry(self) + + def remove(self, rec=1, msg='removed by py lib invocation'): + """ remove a file or directory (or a directory tree if rec=1) with +checkin message msg.""" + if self.rev is not None: + raise py.error.EINVAL(self, "revisions are immutable") + self._svncmdexecauth('svn rm -m "%s" "%s"' %(msg, self._escape(self))) + self._norev_delentry(self.dirpath()) + + def export(self, topath): + """ export to a local path + + topath should not exist prior to calling this, returns a + py.path.local instance + """ + topath = py.path.local(topath) + args = ['"%s"' % (self._escape(self),), + '"%s"' % (self._escape(topath),)] + if self.rev is not None: + args = ['-r', str(self.rev)] + args + self._svncmdexecauth('svn export %s' % (' '.join(args),)) + return topath + + def ensure(self, *args, **kwargs): + """ ensure that an args-joined path exists (by default as + a file). If you specify a keyword argument 'dir=True' + then the path is forced to be a directory path. + """ + if getattr(self, 'rev', None) is not None: + raise py.error.EINVAL(self, "revisions are immutable") + target = self.join(*args) + dir = kwargs.get('dir', 0) + for x in target.parts(reverse=True): + if x.check(): + break + else: + raise py.error.ENOENT(target, "has not any valid base!") + if x == target: + if not x.check(dir=dir): + raise dir and py.error.ENOTDIR(x) or py.error.EISDIR(x) + return x + tocreate = target.relto(x) + basename = tocreate.split(self.sep, 1)[0] + tempdir = py.path.local.mkdtemp() + try: + tempdir.ensure(tocreate, dir=dir) + cmd = 'svn import -m "%s" "%s" "%s"' % ( + "ensure %s" % self._escape(tocreate), + self._escape(tempdir.join(basename)), + x.join(basename)._encodedurl()) + self._svncmdexecauth(cmd) + self._norev_delentry(x) + finally: + tempdir.remove() + return target + + # end of modifying methods + def _propget(self, name): + res = self._svnwithrev('propget', name) + return res[:-1] # strip trailing newline + + def _proplist(self): + res = self._svnwithrev('proplist') + lines = res.split('\n') + lines = [x.strip() for x in lines[1:]] + return svncommon.PropListDict(self, lines) + + def info(self): + """ return an Info structure with svn-provided information. """ + parent = self.dirpath() + nameinfo_seq = parent._listdir_nameinfo() + bn = self.basename + for name, info in nameinfo_seq: + if name == bn: + return info + raise py.error.ENOENT(self) + + + def _listdir_nameinfo(self): + """ return sequence of name-info directory entries of self """ + def builder(): + try: + res = self._svnwithrev('ls', '-v') + except process.cmdexec.Error: + e = sys.exc_info()[1] + if e.err.find('non-existent in that revision') != -1: + raise py.error.ENOENT(self, e.err) + elif e.err.find("E200009:") != -1: + raise py.error.ENOENT(self, e.err) + elif e.err.find('File not found') != -1: + raise py.error.ENOENT(self, e.err) + elif e.err.find('not part of a repository')!=-1: + raise py.error.ENOENT(self, e.err) + elif e.err.find('Unable to open')!=-1: + raise py.error.ENOENT(self, e.err) + elif e.err.lower().find('method not allowed')!=-1: + raise py.error.EACCES(self, e.err) + raise py.error.Error(e.err) + lines = res.split('\n') + nameinfo_seq = [] + for lsline in lines: + if lsline: + info = InfoSvnCommand(lsline) + if info._name != '.': # svn 1.5 produces '.' dirs, + nameinfo_seq.append((info._name, info)) + nameinfo_seq.sort() + return nameinfo_seq + auth = self.auth and self.auth.makecmdoptions() or None + if self.rev is not None: + return self._lsrevcache.getorbuild((self.strpath, self.rev, auth), + builder) + else: + return self._lsnorevcache.getorbuild((self.strpath, auth), + builder) + + def listdir(self, fil=None, sort=None): + """ list directory contents, possibly filter by the given fil func + and possibly sorted. + """ + if isinstance(fil, str): + fil = common.FNMatcher(fil) + nameinfo_seq = self._listdir_nameinfo() + if len(nameinfo_seq) == 1: + name, info = nameinfo_seq[0] + if name == self.basename and info.kind == 'file': + #if not self.check(dir=1): + raise py.error.ENOTDIR(self) + paths = [self.join(name) for (name, info) in nameinfo_seq] + if fil: + paths = [x for x in paths if fil(x)] + self._sortlist(paths, sort) + return paths + + + def log(self, rev_start=None, rev_end=1, verbose=False): + """ return a list of LogEntry instances for this path. +rev_start is the starting revision (defaulting to the first one). +rev_end is the last revision (defaulting to HEAD). +if verbose is True, then the LogEntry instances also know which files changed. +""" + assert self.check() #make it simpler for the pipe + rev_start = rev_start is None and "HEAD" or rev_start + rev_end = rev_end is None and "HEAD" or rev_end + + if rev_start == "HEAD" and rev_end == 1: + rev_opt = "" + else: + rev_opt = "-r %s:%s" % (rev_start, rev_end) + verbose_opt = verbose and "-v" or "" + xmlpipe = self._svnpopenauth('svn log --xml %s %s "%s"' % + (rev_opt, verbose_opt, self.strpath)) + from xml.dom import minidom + tree = minidom.parse(xmlpipe) + result = [] + for logentry in filter(None, tree.firstChild.childNodes): + if logentry.nodeType == logentry.ELEMENT_NODE: + result.append(svncommon.LogEntry(logentry)) + return result + +#01234567890123456789012345678901234567890123467 +# 2256 hpk 165 Nov 24 17:55 __init__.py +# XXX spotted by Guido, SVN 1.3.0 has different aligning, breaks the code!!! +# 1312 johnny 1627 May 05 14:32 test_decorators.py +# +class InfoSvnCommand: + # the '0?' part in the middle is an indication of whether the resource is + # locked, see 'svn help ls' + lspattern = re.compile( + r'^ *(?P<rev>\d+) +(?P<author>.+?) +(0? *(?P<size>\d+))? ' + r'*(?P<date>\w+ +\d{2} +[\d:]+) +(?P<file>.*)$') + def __init__(self, line): + # this is a typical line from 'svn ls http://...' + #_ 1127 jum 0 Jul 13 15:28 branch/ + match = self.lspattern.match(line) + data = match.groupdict() + self._name = data['file'] + if self._name[-1] == '/': + self._name = self._name[:-1] + self.kind = 'dir' + else: + self.kind = 'file' + #self.has_props = l.pop(0) == 'P' + self.created_rev = int(data['rev']) + self.last_author = data['author'] + self.size = data['size'] and int(data['size']) or 0 + self.mtime = parse_time_with_missing_year(data['date']) + self.time = self.mtime * 1000000 + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +#____________________________________________________ +# +# helper functions +#____________________________________________________ +def parse_time_with_missing_year(timestr): + """ analyze the time part from a single line of "svn ls -v" + the svn output doesn't show the year makes the 'timestr' + ambigous. + """ + import calendar + t_now = time.gmtime() + + tparts = timestr.split() + month = time.strptime(tparts.pop(0), '%b')[1] + day = time.strptime(tparts.pop(0), '%d')[2] + last = tparts.pop(0) # year or hour:minute + try: + if ":" in last: + raise ValueError() + year = time.strptime(last, '%Y')[0] + hour = minute = 0 + except ValueError: + hour, minute = time.strptime(last, '%H:%M')[3:5] + year = t_now[0] + + t_result = (year, month, day, hour, minute, 0,0,0,0) + if t_result > t_now: + year -= 1 + t_result = (year, month, day, hour, minute, 0,0,0,0) + return calendar.timegm(t_result) + +class PathEntry: + def __init__(self, ppart): + self.strpath = ppart.firstChild.nodeValue.encode('UTF-8') + self.action = ppart.getAttribute('action').encode('UTF-8') + if self.action == 'A': + self.copyfrom_path = ppart.getAttribute('copyfrom-path').encode('UTF-8') + if self.copyfrom_path: + self.copyfrom_rev = int(ppart.getAttribute('copyfrom-rev')) + diff --git a/venv/lib/python3.5/site-packages/py/_path/svnwc.py b/venv/lib/python3.5/site-packages/py/_path/svnwc.py new file mode 100644 index 0000000..07233b0 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_path/svnwc.py @@ -0,0 +1,1240 @@ +""" +svn-Command based Implementation of a Subversion WorkingCopy Path. + + SvnWCCommandPath is the main class. + +""" + +import os, sys, time, re, calendar +import py +import subprocess +from py._path import common + +#----------------------------------------------------------- +# Caching latest repository revision and repo-paths +# (getting them is slow with the current implementations) +# +# XXX make mt-safe +#----------------------------------------------------------- + +class cache: + proplist = {} + info = {} + entries = {} + prop = {} + +class RepoEntry: + def __init__(self, url, rev, timestamp): + self.url = url + self.rev = rev + self.timestamp = timestamp + + def __str__(self): + return "repo: %s;%s %s" %(self.url, self.rev, self.timestamp) + +class RepoCache: + """ The Repocache manages discovered repository paths + and their revisions. If inside a timeout the cache + will even return the revision of the root. + """ + timeout = 20 # seconds after which we forget that we know the last revision + + def __init__(self): + self.repos = [] + + def clear(self): + self.repos = [] + + def put(self, url, rev, timestamp=None): + if rev is None: + return + if timestamp is None: + timestamp = time.time() + + for entry in self.repos: + if url == entry.url: + entry.timestamp = timestamp + entry.rev = rev + #print "set repo", entry + break + else: + entry = RepoEntry(url, rev, timestamp) + self.repos.append(entry) + #print "appended repo", entry + + def get(self, url): + now = time.time() + for entry in self.repos: + if url.startswith(entry.url): + if now < entry.timestamp + self.timeout: + #print "returning immediate Etrny", entry + return entry.url, entry.rev + return entry.url, -1 + return url, -1 + +repositories = RepoCache() + + +# svn support code + +ALLOWED_CHARS = "_ -/\\=$.~+%" #add characters as necessary when tested +if sys.platform == "win32": + ALLOWED_CHARS += ":" +ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:' + +def _getsvnversion(ver=[]): + try: + return ver[0] + except IndexError: + v = py.process.cmdexec("svn -q --version") + v.strip() + v = '.'.join(v.split('.')[:2]) + ver.append(v) + return v + +def _escape_helper(text): + text = str(text) + if py.std.sys.platform != 'win32': + text = str(text).replace('$', '\\$') + return text + +def _check_for_bad_chars(text, allowed_chars=ALLOWED_CHARS): + for c in str(text): + if c.isalnum(): + continue + if c in allowed_chars: + continue + return True + return False + +def checkbadchars(url): + # (hpk) not quite sure about the exact purpose, guido w.? + proto, uri = url.split("://", 1) + if proto != "file": + host, uripath = uri.split('/', 1) + # only check for bad chars in the non-protocol parts + if (_check_for_bad_chars(host, ALLOWED_CHARS_HOST) \ + or _check_for_bad_chars(uripath, ALLOWED_CHARS)): + raise ValueError("bad char in %r" % (url, )) + + +#_______________________________________________________________ + +class SvnPathBase(common.PathBase): + """ Base implementation for SvnPath implementations. """ + sep = '/' + + def _geturl(self): + return self.strpath + url = property(_geturl, None, None, "url of this svn-path.") + + def __str__(self): + """ return a string representation (including rev-number) """ + return self.strpath + + def __hash__(self): + return hash(self.strpath) + + def new(self, **kw): + """ create a modified version of this path. A 'rev' argument + indicates a new revision. + the following keyword arguments modify various path parts:: + + http://host.com/repo/path/file.ext + |-----------------------| dirname + |------| basename + |--| purebasename + |--| ext + """ + obj = object.__new__(self.__class__) + obj.rev = kw.get('rev', self.rev) + obj.auth = kw.get('auth', self.auth) + dirname, basename, purebasename, ext = self._getbyspec( + "dirname,basename,purebasename,ext") + if 'basename' in kw: + if 'purebasename' in kw or 'ext' in kw: + raise ValueError("invalid specification %r" % kw) + else: + pb = kw.setdefault('purebasename', purebasename) + ext = kw.setdefault('ext', ext) + if ext and not ext.startswith('.'): + ext = '.' + ext + kw['basename'] = pb + ext + + kw.setdefault('dirname', dirname) + kw.setdefault('sep', self.sep) + if kw['basename']: + obj.strpath = "%(dirname)s%(sep)s%(basename)s" % kw + else: + obj.strpath = "%(dirname)s" % kw + return obj + + def _getbyspec(self, spec): + """ get specified parts of the path. 'arg' is a string + with comma separated path parts. The parts are returned + in exactly the order of the specification. + + you may specify the following parts: + + http://host.com/repo/path/file.ext + |-----------------------| dirname + |------| basename + |--| purebasename + |--| ext + """ + res = [] + parts = self.strpath.split(self.sep) + for name in spec.split(','): + name = name.strip() + if name == 'dirname': + res.append(self.sep.join(parts[:-1])) + elif name == 'basename': + res.append(parts[-1]) + else: + basename = parts[-1] + i = basename.rfind('.') + if i == -1: + purebasename, ext = basename, '' + else: + purebasename, ext = basename[:i], basename[i:] + if name == 'purebasename': + res.append(purebasename) + elif name == 'ext': + res.append(ext) + else: + raise NameError("Don't know part %r" % name) + return res + + def __eq__(self, other): + """ return true if path and rev attributes each match """ + return (str(self) == str(other) and + (self.rev == other.rev or self.rev == other.rev)) + + def __ne__(self, other): + return not self == other + + def join(self, *args): + """ return a new Path (with the same revision) which is composed + of the self Path followed by 'args' path components. + """ + if not args: + return self + + args = tuple([arg.strip(self.sep) for arg in args]) + parts = (self.strpath, ) + args + newpath = self.__class__(self.sep.join(parts), self.rev, self.auth) + return newpath + + def propget(self, name): + """ return the content of the given property. """ + value = self._propget(name) + return value + + def proplist(self): + """ list all property names. """ + content = self._proplist() + return content + + def size(self): + """ Return the size of the file content of the Path. """ + return self.info().size + + def mtime(self): + """ Return the last modification time of the file. """ + return self.info().mtime + + # shared help methods + + def _escape(self, cmd): + return _escape_helper(cmd) + + + #def _childmaxrev(self): + # """ return maximum revision number of childs (or self.rev if no childs) """ + # rev = self.rev + # for name, info in self._listdir_nameinfo(): + # rev = max(rev, info.created_rev) + # return rev + + #def _getlatestrevision(self): + # """ return latest repo-revision for this path. """ + # url = self.strpath + # path = self.__class__(url, None) + # + # # we need a long walk to find the root-repo and revision + # while 1: + # try: + # rev = max(rev, path._childmaxrev()) + # previous = path + # path = path.dirpath() + # except (IOError, process.cmdexec.Error): + # break + # if rev is None: + # raise IOError, "could not determine newest repo revision for %s" % self + # return rev + + class Checkers(common.Checkers): + def dir(self): + try: + return self.path.info().kind == 'dir' + except py.error.Error: + return self._listdirworks() + + def _listdirworks(self): + try: + self.path.listdir() + except py.error.ENOENT: + return False + else: + return True + + def file(self): + try: + return self.path.info().kind == 'file' + except py.error.ENOENT: + return False + + def exists(self): + try: + return self.path.info() + except py.error.ENOENT: + return self._listdirworks() + +def parse_apr_time(timestr): + i = timestr.rfind('.') + if i == -1: + raise ValueError("could not parse %s" % timestr) + timestr = timestr[:i] + parsedtime = time.strptime(timestr, "%Y-%m-%dT%H:%M:%S") + return time.mktime(parsedtime) + +class PropListDict(dict): + """ a Dictionary which fetches values (InfoSvnCommand instances) lazily""" + def __init__(self, path, keynames): + dict.__init__(self, [(x, None) for x in keynames]) + self.path = path + + def __getitem__(self, key): + value = dict.__getitem__(self, key) + if value is None: + value = self.path.propget(key) + dict.__setitem__(self, key, value) + return value + +def fixlocale(): + if sys.platform != 'win32': + return 'LC_ALL=C ' + return '' + +# some nasty chunk of code to solve path and url conversion and quoting issues +ILLEGAL_CHARS = '* | \\ / : < > ? \t \n \x0b \x0c \r'.split(' ') +if os.sep in ILLEGAL_CHARS: + ILLEGAL_CHARS.remove(os.sep) +ISWINDOWS = sys.platform == 'win32' +_reg_allow_disk = re.compile(r'^([a-z]\:\\)?[^:]+$', re.I) +def _check_path(path): + illegal = ILLEGAL_CHARS[:] + sp = path.strpath + if ISWINDOWS: + illegal.remove(':') + if not _reg_allow_disk.match(sp): + raise ValueError('path may not contain a colon (:)') + for char in sp: + if char not in string.printable or char in illegal: + raise ValueError('illegal character %r in path' % (char,)) + +def path_to_fspath(path, addat=True): + _check_path(path) + sp = path.strpath + if addat and path.rev != -1: + sp = '%s@%s' % (sp, path.rev) + elif addat: + sp = '%s@HEAD' % (sp,) + return sp + +def url_from_path(path): + fspath = path_to_fspath(path, False) + quote = py.std.urllib.quote + if ISWINDOWS: + match = _reg_allow_disk.match(fspath) + fspath = fspath.replace('\\', '/') + if match.group(1): + fspath = '/%s%s' % (match.group(1).replace('\\', '/'), + quote(fspath[len(match.group(1)):])) + else: + fspath = quote(fspath) + else: + fspath = quote(fspath) + if path.rev != -1: + fspath = '%s@%s' % (fspath, path.rev) + else: + fspath = '%s@HEAD' % (fspath,) + return 'file://%s' % (fspath,) + +class SvnAuth(object): + """ container for auth information for Subversion """ + def __init__(self, username, password, cache_auth=True, interactive=True): + self.username = username + self.password = password + self.cache_auth = cache_auth + self.interactive = interactive + + def makecmdoptions(self): + uname = self.username.replace('"', '\\"') + passwd = self.password.replace('"', '\\"') + ret = [] + if uname: + ret.append('--username="%s"' % (uname,)) + if passwd: + ret.append('--password="%s"' % (passwd,)) + if not self.cache_auth: + ret.append('--no-auth-cache') + if not self.interactive: + ret.append('--non-interactive') + return ' '.join(ret) + + def __str__(self): + return "<SvnAuth username=%s ...>" %(self.username,) + +rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)') + +class SvnWCCommandPath(common.PathBase): + """ path implementation offering access/modification to svn working copies. + It has methods similar to the functions in os.path and similar to the + commands of the svn client. + """ + sep = os.sep + + def __new__(cls, wcpath=None, auth=None): + self = object.__new__(cls) + if isinstance(wcpath, cls): + if wcpath.__class__ == cls: + return wcpath + wcpath = wcpath.localpath + if _check_for_bad_chars(str(wcpath), + ALLOWED_CHARS): + raise ValueError("bad char in wcpath %s" % (wcpath, )) + self.localpath = py.path.local(wcpath) + self.auth = auth + return self + + strpath = property(lambda x: str(x.localpath), None, None, "string path") + rev = property(lambda x: x.info(usecache=0).rev, None, None, "revision") + + def __eq__(self, other): + return self.localpath == getattr(other, 'localpath', None) + + def _geturl(self): + if getattr(self, '_url', None) is None: + info = self.info() + self._url = info.url #SvnPath(info.url, info.rev) + assert isinstance(self._url, py.builtin._basestring) + return self._url + + url = property(_geturl, None, None, "url of this WC item") + + def _escape(self, cmd): + return _escape_helper(cmd) + + def dump(self, obj): + """ pickle object into path location""" + return self.localpath.dump(obj) + + def svnurl(self): + """ return current SvnPath for this WC-item. """ + info = self.info() + return py.path.svnurl(info.url) + + def __repr__(self): + return "svnwc(%r)" % (self.strpath) # , self._url) + + def __str__(self): + return str(self.localpath) + + def _makeauthoptions(self): + if self.auth is None: + return '' + return self.auth.makecmdoptions() + + def _authsvn(self, cmd, args=None): + args = args and list(args) or [] + args.append(self._makeauthoptions()) + return self._svn(cmd, *args) + + def _svn(self, cmd, *args): + l = ['svn %s' % cmd] + args = [self._escape(item) for item in args] + l.extend(args) + l.append('"%s"' % self._escape(self.strpath)) + # try fixing the locale because we can't otherwise parse + string = fixlocale() + " ".join(l) + try: + try: + key = 'LC_MESSAGES' + hold = os.environ.get(key) + os.environ[key] = 'C' + out = py.process.cmdexec(string) + finally: + if hold: + os.environ[key] = hold + else: + del os.environ[key] + except py.process.cmdexec.Error: + e = sys.exc_info()[1] + strerr = e.err.lower() + if strerr.find('not found') != -1: + raise py.error.ENOENT(self) + elif strerr.find("E200009:") != -1: + raise py.error.ENOENT(self) + if (strerr.find('file exists') != -1 or + strerr.find('file already exists') != -1 or + strerr.find('w150002:') != -1 or + strerr.find("can't create directory") != -1): + raise py.error.EEXIST(strerr) #self) + raise + return out + + def switch(self, url): + """ switch to given URL. """ + self._authsvn('switch', [url]) + + def checkout(self, url=None, rev=None): + """ checkout from url to local wcpath. """ + args = [] + if url is None: + url = self.url + if rev is None or rev == -1: + if (py.std.sys.platform != 'win32' and + _getsvnversion() == '1.3'): + url += "@HEAD" + else: + if _getsvnversion() == '1.3': + url += "@%d" % rev + else: + args.append('-r' + str(rev)) + args.append(url) + self._authsvn('co', args) + + def update(self, rev='HEAD', interactive=True): + """ update working copy item to given revision. (None -> HEAD). """ + opts = ['-r', rev] + if not interactive: + opts.append("--non-interactive") + self._authsvn('up', opts) + + def write(self, content, mode='w'): + """ write content into local filesystem wc. """ + self.localpath.write(content, mode) + + def dirpath(self, *args): + """ return the directory Path of the current Path. """ + return self.__class__(self.localpath.dirpath(*args), auth=self.auth) + + def _ensuredirs(self): + parent = self.dirpath() + if parent.check(dir=0): + parent._ensuredirs() + if self.check(dir=0): + self.mkdir() + return self + + def ensure(self, *args, **kwargs): + """ ensure that an args-joined path exists (by default as + a file). if you specify a keyword argument 'directory=True' + then the path is forced to be a directory path. + """ + p = self.join(*args) + if p.check(): + if p.check(versioned=False): + p.add() + return p + if kwargs.get('dir', 0): + return p._ensuredirs() + parent = p.dirpath() + parent._ensuredirs() + p.write("") + p.add() + return p + + def mkdir(self, *args): + """ create & return the directory joined with args. """ + if args: + return self.join(*args).mkdir() + else: + self._svn('mkdir') + return self + + def add(self): + """ add ourself to svn """ + self._svn('add') + + def remove(self, rec=1, force=1): + """ remove a file or a directory tree. 'rec'ursive is + ignored and considered always true (because of + underlying svn semantics. + """ + assert rec, "svn cannot remove non-recursively" + if not self.check(versioned=True): + # not added to svn (anymore?), just remove + py.path.local(self).remove() + return + flags = [] + if force: + flags.append('--force') + self._svn('remove', *flags) + + def copy(self, target): + """ copy path to target.""" + py.process.cmdexec("svn copy %s %s" %(str(self), str(target))) + + def rename(self, target): + """ rename this path to target. """ + py.process.cmdexec("svn move --force %s %s" %(str(self), str(target))) + + def lock(self): + """ set a lock (exclusive) on the resource """ + out = self._authsvn('lock').strip() + if not out: + # warning or error, raise exception + raise ValueError("unknown error in svn lock command") + + def unlock(self): + """ unset a previously set lock """ + out = self._authsvn('unlock').strip() + if out.startswith('svn:'): + # warning or error, raise exception + raise Exception(out[4:]) + + def cleanup(self): + """ remove any locks from the resource """ + # XXX should be fixed properly!!! + try: + self.unlock() + except: + pass + + def status(self, updates=0, rec=0, externals=0): + """ return (collective) Status object for this file. """ + # http://svnbook.red-bean.com/book.html#svn-ch-3-sect-4.3.1 + # 2201 2192 jum test + # XXX + if externals: + raise ValueError("XXX cannot perform status() " + "on external items yet") + else: + #1.2 supports: externals = '--ignore-externals' + externals = '' + if rec: + rec= '' + else: + rec = '--non-recursive' + + # XXX does not work on all subversion versions + #if not externals: + # externals = '--ignore-externals' + + if updates: + updates = '-u' + else: + updates = '' + + try: + cmd = 'status -v --xml --no-ignore %s %s %s' % ( + updates, rec, externals) + out = self._authsvn(cmd) + except py.process.cmdexec.Error: + cmd = 'status -v --no-ignore %s %s %s' % ( + updates, rec, externals) + out = self._authsvn(cmd) + rootstatus = WCStatus(self).fromstring(out, self) + else: + rootstatus = XMLWCStatus(self).fromstring(out, self) + return rootstatus + + def diff(self, rev=None): + """ return a diff of the current path against revision rev (defaulting + to the last one). + """ + args = [] + if rev is not None: + args.append("-r %d" % rev) + out = self._authsvn('diff', args) + return out + + def blame(self): + """ return a list of tuples of three elements: + (revision, commiter, line) + """ + out = self._svn('blame') + result = [] + blamelines = out.splitlines() + reallines = py.path.svnurl(self.url).readlines() + for i, (blameline, line) in enumerate( + zip(blamelines, reallines)): + m = rex_blame.match(blameline) + if not m: + raise ValueError("output line %r of svn blame does not match " + "expected format" % (line, )) + rev, name, _ = m.groups() + result.append((int(rev), name, line)) + return result + + _rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL) + def commit(self, msg='', rec=1): + """ commit with support for non-recursive commits """ + # XXX i guess escaping should be done better here?!? + cmd = 'commit -m "%s" --force-log' % (msg.replace('"', '\\"'),) + if not rec: + cmd += ' -N' + out = self._authsvn(cmd) + try: + del cache.info[self] + except KeyError: + pass + if out: + m = self._rex_commit.match(out) + return int(m.group(1)) + + def propset(self, name, value, *args): + """ set property name to value on this path. """ + d = py.path.local.mkdtemp() + try: + p = d.join('value') + p.write(value) + self._svn('propset', name, '--file', str(p), *args) + finally: + d.remove() + + def propget(self, name): + """ get property name on this path. """ + res = self._svn('propget', name) + return res[:-1] # strip trailing newline + + def propdel(self, name): + """ delete property name on this path. """ + res = self._svn('propdel', name) + return res[:-1] # strip trailing newline + + def proplist(self, rec=0): + """ return a mapping of property names to property values. +If rec is True, then return a dictionary mapping sub-paths to such mappings. +""" + if rec: + res = self._svn('proplist -R') + return make_recursive_propdict(self, res) + else: + res = self._svn('proplist') + lines = res.split('\n') + lines = [x.strip() for x in lines[1:]] + return PropListDict(self, lines) + + def revert(self, rec=0): + """ revert the local changes of this path. if rec is True, do so +recursively. """ + if rec: + result = self._svn('revert -R') + else: + result = self._svn('revert') + return result + + def new(self, **kw): + """ create a modified version of this path. A 'rev' argument + indicates a new revision. + the following keyword arguments modify various path parts: + + http://host.com/repo/path/file.ext + |-----------------------| dirname + |------| basename + |--| purebasename + |--| ext + """ + if kw: + localpath = self.localpath.new(**kw) + else: + localpath = self.localpath + return self.__class__(localpath, auth=self.auth) + + def join(self, *args, **kwargs): + """ return a new Path (with the same revision) which is composed + of the self Path followed by 'args' path components. + """ + if not args: + return self + localpath = self.localpath.join(*args, **kwargs) + return self.__class__(localpath, auth=self.auth) + + def info(self, usecache=1): + """ return an Info structure with svn-provided information. """ + info = usecache and cache.info.get(self) + if not info: + try: + output = self._svn('info') + except py.process.cmdexec.Error: + e = sys.exc_info()[1] + if e.err.find('Path is not a working copy directory') != -1: + raise py.error.ENOENT(self, e.err) + elif e.err.find("is not under version control") != -1: + raise py.error.ENOENT(self, e.err) + raise + # XXX SVN 1.3 has output on stderr instead of stdout (while it does + # return 0!), so a bit nasty, but we assume no output is output + # to stderr... + if (output.strip() == '' or + output.lower().find('not a versioned resource') != -1): + raise py.error.ENOENT(self, output) + info = InfoSvnWCCommand(output) + + # Can't reliably compare on Windows without access to win32api + if py.std.sys.platform != 'win32': + if info.path != self.localpath: + raise py.error.ENOENT(self, "not a versioned resource:" + + " %s != %s" % (info.path, self.localpath)) + cache.info[self] = info + return info + + def listdir(self, fil=None, sort=None): + """ return a sequence of Paths. + + listdir will return either a tuple or a list of paths + depending on implementation choices. + """ + if isinstance(fil, str): + fil = common.FNMatcher(fil) + # XXX unify argument naming with LocalPath.listdir + def notsvn(path): + return path.basename != '.svn' + + paths = [] + for localpath in self.localpath.listdir(notsvn): + p = self.__class__(localpath, auth=self.auth) + if notsvn(p) and (not fil or fil(p)): + paths.append(p) + self._sortlist(paths, sort) + return paths + + def open(self, mode='r'): + """ return an opened file with the given mode. """ + return open(self.strpath, mode) + + def _getbyspec(self, spec): + return self.localpath._getbyspec(spec) + + class Checkers(py.path.local.Checkers): + def __init__(self, path): + self.svnwcpath = path + self.path = path.localpath + def versioned(self): + try: + s = self.svnwcpath.info() + except (py.error.ENOENT, py.error.EEXIST): + return False + except py.process.cmdexec.Error: + e = sys.exc_info()[1] + if e.err.find('is not a working copy')!=-1: + return False + if e.err.lower().find('not a versioned resource') != -1: + return False + raise + else: + return True + + def log(self, rev_start=None, rev_end=1, verbose=False): + """ return a list of LogEntry instances for this path. +rev_start is the starting revision (defaulting to the first one). +rev_end is the last revision (defaulting to HEAD). +if verbose is True, then the LogEntry instances also know which files changed. +""" + assert self.check() # make it simpler for the pipe + rev_start = rev_start is None and "HEAD" or rev_start + rev_end = rev_end is None and "HEAD" or rev_end + if rev_start == "HEAD" and rev_end == 1: + rev_opt = "" + else: + rev_opt = "-r %s:%s" % (rev_start, rev_end) + verbose_opt = verbose and "-v" or "" + locale_env = fixlocale() + # some blather on stderr + auth_opt = self._makeauthoptions() + #stdin, stdout, stderr = os.popen3(locale_env + + # 'svn log --xml %s %s %s "%s"' % ( + # rev_opt, verbose_opt, auth_opt, + # self.strpath)) + cmd = locale_env + 'svn log --xml %s %s %s "%s"' % ( + rev_opt, verbose_opt, auth_opt, self.strpath) + + popen = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, + ) + stdout, stderr = popen.communicate() + stdout = py.builtin._totext(stdout, sys.getdefaultencoding()) + minidom,ExpatError = importxml() + try: + tree = minidom.parseString(stdout) + except ExpatError: + raise ValueError('no such revision') + result = [] + for logentry in filter(None, tree.firstChild.childNodes): + if logentry.nodeType == logentry.ELEMENT_NODE: + result.append(LogEntry(logentry)) + return result + + def size(self): + """ Return the size of the file content of the Path. """ + return self.info().size + + def mtime(self): + """ Return the last modification time of the file. """ + return self.info().mtime + + def __hash__(self): + return hash((self.strpath, self.__class__, self.auth)) + + +class WCStatus: + attrnames = ('modified','added', 'conflict', 'unchanged', 'external', + 'deleted', 'prop_modified', 'unknown', 'update_available', + 'incomplete', 'kindmismatch', 'ignored', 'locked', 'replaced' + ) + + def __init__(self, wcpath, rev=None, modrev=None, author=None): + self.wcpath = wcpath + self.rev = rev + self.modrev = modrev + self.author = author + + for name in self.attrnames: + setattr(self, name, []) + + def allpath(self, sort=True, **kw): + d = {} + for name in self.attrnames: + if name not in kw or kw[name]: + for path in getattr(self, name): + d[path] = 1 + l = d.keys() + if sort: + l.sort() + return l + + # XXX a bit scary to assume there's always 2 spaces between username and + # path, however with win32 allowing spaces in user names there doesn't + # seem to be a more solid approach :( + _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') + + def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): + """ return a new WCStatus object from data 's' + """ + rootstatus = WCStatus(rootwcpath, rev, modrev, author) + update_rev = None + for line in data.split('\n'): + if not line.strip(): + continue + #print "processing %r" % line + flags, rest = line[:8], line[8:] + # first column + c0,c1,c2,c3,c4,c5,x6,c7 = flags + #if '*' in line: + # print "flags", repr(flags), "rest", repr(rest) + + if c0 in '?XI': + fn = line.split(None, 1)[1] + if c0 == '?': + wcpath = rootwcpath.join(fn, abs=1) + rootstatus.unknown.append(wcpath) + elif c0 == 'X': + wcpath = rootwcpath.__class__( + rootwcpath.localpath.join(fn, abs=1), + auth=rootwcpath.auth) + rootstatus.external.append(wcpath) + elif c0 == 'I': + wcpath = rootwcpath.join(fn, abs=1) + rootstatus.ignored.append(wcpath) + + continue + + #elif c0 in '~!' or c4 == 'S': + # raise NotImplementedError("received flag %r" % c0) + + m = WCStatus._rex_status.match(rest) + if not m: + if c7 == '*': + fn = rest.strip() + wcpath = rootwcpath.join(fn, abs=1) + rootstatus.update_available.append(wcpath) + continue + if line.lower().find('against revision:')!=-1: + update_rev = int(rest.split(':')[1].strip()) + continue + if line.lower().find('status on external') > -1: + # XXX not sure what to do here... perhaps we want to + # store some state instead of just continuing, as right + # now it makes the top-level external get added twice + # (once as external, once as 'normal' unchanged item) + # because of the way SVN presents external items + continue + # keep trying + raise ValueError("could not parse line %r" % line) + else: + rev, modrev, author, fn = m.groups() + wcpath = rootwcpath.join(fn, abs=1) + #assert wcpath.check() + if c0 == 'M': + assert wcpath.check(file=1), "didn't expect a directory with changed content here" + rootstatus.modified.append(wcpath) + elif c0 == 'A' or c3 == '+' : + rootstatus.added.append(wcpath) + elif c0 == 'D': + rootstatus.deleted.append(wcpath) + elif c0 == 'C': + rootstatus.conflict.append(wcpath) + elif c0 == '~': + rootstatus.kindmismatch.append(wcpath) + elif c0 == '!': + rootstatus.incomplete.append(wcpath) + elif c0 == 'R': + rootstatus.replaced.append(wcpath) + elif not c0.strip(): + rootstatus.unchanged.append(wcpath) + else: + raise NotImplementedError("received flag %r" % c0) + + if c1 == 'M': + rootstatus.prop_modified.append(wcpath) + # XXX do we cover all client versions here? + if c2 == 'L' or c5 == 'K': + rootstatus.locked.append(wcpath) + if c7 == '*': + rootstatus.update_available.append(wcpath) + + if wcpath == rootwcpath: + rootstatus.rev = rev + rootstatus.modrev = modrev + rootstatus.author = author + if update_rev: + rootstatus.update_rev = update_rev + continue + return rootstatus + fromstring = staticmethod(fromstring) + +class XMLWCStatus(WCStatus): + def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): + """ parse 'data' (XML string as outputted by svn st) into a status obj + """ + # XXX for externals, the path is shown twice: once + # with external information, and once with full info as if + # the item was a normal non-external... the current way of + # dealing with this issue is by ignoring it - this does make + # externals appear as external items as well as 'normal', + # unchanged ones in the status object so this is far from ideal + rootstatus = WCStatus(rootwcpath, rev, modrev, author) + update_rev = None + minidom, ExpatError = importxml() + try: + doc = minidom.parseString(data) + except ExpatError: + e = sys.exc_info()[1] + raise ValueError(str(e)) + urevels = doc.getElementsByTagName('against') + if urevels: + rootstatus.update_rev = urevels[-1].getAttribute('revision') + for entryel in doc.getElementsByTagName('entry'): + path = entryel.getAttribute('path') + statusel = entryel.getElementsByTagName('wc-status')[0] + itemstatus = statusel.getAttribute('item') + + if itemstatus == 'unversioned': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.unknown.append(wcpath) + continue + elif itemstatus == 'external': + wcpath = rootwcpath.__class__( + rootwcpath.localpath.join(path, abs=1), + auth=rootwcpath.auth) + rootstatus.external.append(wcpath) + continue + elif itemstatus == 'ignored': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.ignored.append(wcpath) + continue + elif itemstatus == 'incomplete': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.incomplete.append(wcpath) + continue + + rev = statusel.getAttribute('revision') + if itemstatus == 'added' or itemstatus == 'none': + rev = '0' + modrev = '?' + author = '?' + date = '' + elif itemstatus == "replaced": + pass + else: + #print entryel.toxml() + commitel = entryel.getElementsByTagName('commit')[0] + if commitel: + modrev = commitel.getAttribute('revision') + author = '' + author_els = commitel.getElementsByTagName('author') + if author_els: + for c in author_els[0].childNodes: + author += c.nodeValue + date = '' + for c in commitel.getElementsByTagName('date')[0]\ + .childNodes: + date += c.nodeValue + + wcpath = rootwcpath.join(path, abs=1) + + assert itemstatus != 'modified' or wcpath.check(file=1), ( + 'did\'t expect a directory with changed content here') + + itemattrname = { + 'normal': 'unchanged', + 'unversioned': 'unknown', + 'conflicted': 'conflict', + 'none': 'added', + }.get(itemstatus, itemstatus) + + attr = getattr(rootstatus, itemattrname) + attr.append(wcpath) + + propsstatus = statusel.getAttribute('props') + if propsstatus not in ('none', 'normal'): + rootstatus.prop_modified.append(wcpath) + + if wcpath == rootwcpath: + rootstatus.rev = rev + rootstatus.modrev = modrev + rootstatus.author = author + rootstatus.date = date + + # handle repos-status element (remote info) + rstatusels = entryel.getElementsByTagName('repos-status') + if rstatusels: + rstatusel = rstatusels[0] + ritemstatus = rstatusel.getAttribute('item') + if ritemstatus in ('added', 'modified'): + rootstatus.update_available.append(wcpath) + + lockels = entryel.getElementsByTagName('lock') + if len(lockels): + rootstatus.locked.append(wcpath) + + return rootstatus + fromstring = staticmethod(fromstring) + +class InfoSvnWCCommand: + def __init__(self, output): + # Path: test + # URL: http://codespeak.net/svn/std.path/trunk/dist/std.path/test + # Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada + # Revision: 2151 + # Node Kind: directory + # Schedule: normal + # Last Changed Author: hpk + # Last Changed Rev: 2100 + # Last Changed Date: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003) + # Properties Last Updated: 2003-11-03 14:47:48 +0100 (Mon, 03 Nov 2003) + + d = {} + for line in output.split('\n'): + if not line.strip(): + continue + key, value = line.split(':', 1) + key = key.lower().replace(' ', '') + value = value.strip() + d[key] = value + try: + self.url = d['url'] + except KeyError: + raise ValueError("Not a versioned resource") + #raise ValueError, "Not a versioned resource %r" % path + self.kind = d['nodekind'] == 'directory' and 'dir' or d['nodekind'] + try: + self.rev = int(d['revision']) + except KeyError: + self.rev = None + + self.path = py.path.local(d['path']) + self.size = self.path.size() + if 'lastchangedrev' in d: + self.created_rev = int(d['lastchangedrev']) + if 'lastchangedauthor' in d: + self.last_author = d['lastchangedauthor'] + if 'lastchangeddate' in d: + self.mtime = parse_wcinfotime(d['lastchangeddate']) + self.time = self.mtime * 1000000 + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + +def parse_wcinfotime(timestr): + """ Returns seconds since epoch, UTC. """ + # example: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003) + m = re.match(r'(\d+-\d+-\d+ \d+:\d+:\d+) ([+-]\d+) .*', timestr) + if not m: + raise ValueError("timestring %r does not match" % timestr) + timestr, timezone = m.groups() + # do not handle timezone specially, return value should be UTC + parsedtime = time.strptime(timestr, "%Y-%m-%d %H:%M:%S") + return calendar.timegm(parsedtime) + +def make_recursive_propdict(wcroot, + output, + rex = re.compile("Properties on '(.*)':")): + """ Return a dictionary of path->PropListDict mappings. """ + lines = [x for x in output.split('\n') if x] + pdict = {} + while lines: + line = lines.pop(0) + m = rex.match(line) + if not m: + raise ValueError("could not parse propget-line: %r" % line) + path = m.groups()[0] + wcpath = wcroot.join(path, abs=1) + propnames = [] + while lines and lines[0].startswith(' '): + propname = lines.pop(0).strip() + propnames.append(propname) + assert propnames, "must have found properties!" + pdict[wcpath] = PropListDict(wcpath, propnames) + return pdict + + +def importxml(cache=[]): + if cache: + return cache + from xml.dom import minidom + from xml.parsers.expat import ExpatError + cache.extend([minidom, ExpatError]) + return cache + +class LogEntry: + def __init__(self, logentry): + self.rev = int(logentry.getAttribute('revision')) + for lpart in filter(None, logentry.childNodes): + if lpart.nodeType == lpart.ELEMENT_NODE: + if lpart.nodeName == 'author': + self.author = lpart.firstChild.nodeValue + elif lpart.nodeName == 'msg': + if lpart.firstChild: + self.msg = lpart.firstChild.nodeValue + else: + self.msg = '' + elif lpart.nodeName == 'date': + #2003-07-29T20:05:11.598637Z + timestr = lpart.firstChild.nodeValue + self.date = parse_apr_time(timestr) + elif lpart.nodeName == 'paths': + self.strpaths = [] + for ppart in filter(None, lpart.childNodes): + if ppart.nodeType == ppart.ELEMENT_NODE: + self.strpaths.append(PathEntry(ppart)) + def __repr__(self): + return '<Logentry rev=%d author=%s date=%s>' % ( + self.rev, self.author, self.date) + + diff --git a/venv/lib/python3.5/site-packages/py/_process/__init__.py b/venv/lib/python3.5/site-packages/py/_process/__init__.py new file mode 100644 index 0000000..3186a2d --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_process/__init__.py @@ -0,0 +1 @@ +""" high-level sub-process handling """ diff --git a/venv/lib/python3.5/site-packages/py/_process/cmdexec.py b/venv/lib/python3.5/site-packages/py/_process/cmdexec.py new file mode 100644 index 0000000..22a1a82 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_process/cmdexec.py @@ -0,0 +1,49 @@ +import sys +import subprocess +import py +from subprocess import Popen, PIPE + +def cmdexec(cmd): + """ return unicode output of executing 'cmd' in a separate process. + + raise cmdexec.Error exeception if the command failed. + the exception will provide an 'err' attribute containing + the error-output from the command. + if the subprocess module does not provide a proper encoding/unicode strings + sys.getdefaultencoding() will be used, if that does not exist, 'UTF-8'. + """ + process = subprocess.Popen(cmd, shell=True, + universal_newlines=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = process.communicate() + if sys.version_info[0] < 3: # on py3 we get unicode strings, on py2 not + try: + default_encoding = sys.getdefaultencoding() # jython may not have it + except AttributeError: + default_encoding = sys.stdout.encoding or 'UTF-8' + out = unicode(out, process.stdout.encoding or default_encoding) + err = unicode(err, process.stderr.encoding or default_encoding) + status = process.poll() + if status: + raise ExecutionFailed(status, status, cmd, out, err) + return out + +class ExecutionFailed(py.error.Error): + def __init__(self, status, systemstatus, cmd, out, err): + Exception.__init__(self) + self.status = status + self.systemstatus = systemstatus + self.cmd = cmd + self.err = err + self.out = out + + def __str__(self): + return "ExecutionFailed: %d %s\n%s" %(self.status, self.cmd, self.err) + +# export the exception under the name 'py.process.cmdexec.Error' +cmdexec.Error = ExecutionFailed +try: + ExecutionFailed.__module__ = 'py.process.cmdexec' + ExecutionFailed.__name__ = 'Error' +except (AttributeError, TypeError): + pass diff --git a/venv/lib/python3.5/site-packages/py/_process/forkedfunc.py b/venv/lib/python3.5/site-packages/py/_process/forkedfunc.py new file mode 100644 index 0000000..d23e653 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_process/forkedfunc.py @@ -0,0 +1,120 @@ + +""" + ForkedFunc provides a way to run a function in a forked process + and get at its return value, stdout and stderr output as well + as signals and exitstatusus. +""" + +import py +import os +import sys +import marshal + + +def get_unbuffered_io(fd, filename): + f = open(str(filename), "w") + if fd != f.fileno(): + os.dup2(f.fileno(), fd) + class AutoFlush: + def write(self, data): + f.write(data) + f.flush() + def __getattr__(self, name): + return getattr(f, name) + return AutoFlush() + + +class ForkedFunc: + EXITSTATUS_EXCEPTION = 3 + + + def __init__(self, fun, args=None, kwargs=None, nice_level=0, + child_on_start=None, child_on_exit=None): + if args is None: + args = [] + if kwargs is None: + kwargs = {} + self.fun = fun + self.args = args + self.kwargs = kwargs + self.tempdir = tempdir = py.path.local.mkdtemp() + self.RETVAL = tempdir.ensure('retval') + self.STDOUT = tempdir.ensure('stdout') + self.STDERR = tempdir.ensure('stderr') + + pid = os.fork() + if pid: # in parent process + self.pid = pid + else: # in child process + self.pid = None + self._child(nice_level, child_on_start, child_on_exit) + + def _child(self, nice_level, child_on_start, child_on_exit): + # right now we need to call a function, but first we need to + # map all IO that might happen + sys.stdout = stdout = get_unbuffered_io(1, self.STDOUT) + sys.stderr = stderr = get_unbuffered_io(2, self.STDERR) + retvalf = self.RETVAL.open("wb") + EXITSTATUS = 0 + try: + if nice_level: + os.nice(nice_level) + try: + if child_on_start is not None: + child_on_start() + retval = self.fun(*self.args, **self.kwargs) + retvalf.write(marshal.dumps(retval)) + if child_on_exit is not None: + child_on_exit() + except: + excinfo = py.code.ExceptionInfo() + stderr.write(str(excinfo._getreprcrash())) + EXITSTATUS = self.EXITSTATUS_EXCEPTION + finally: + stdout.close() + stderr.close() + retvalf.close() + os.close(1) + os.close(2) + os._exit(EXITSTATUS) + + def waitfinish(self, waiter=os.waitpid): + pid, systemstatus = waiter(self.pid, 0) + if systemstatus: + if os.WIFSIGNALED(systemstatus): + exitstatus = os.WTERMSIG(systemstatus) + 128 + else: + exitstatus = os.WEXITSTATUS(systemstatus) + else: + exitstatus = 0 + signal = systemstatus & 0x7f + if not exitstatus and not signal: + retval = self.RETVAL.open('rb') + try: + retval_data = retval.read() + finally: + retval.close() + retval = marshal.loads(retval_data) + else: + retval = None + stdout = self.STDOUT.read() + stderr = self.STDERR.read() + self._removetemp() + return Result(exitstatus, signal, retval, stdout, stderr) + + def _removetemp(self): + if self.tempdir.check(): + self.tempdir.remove() + + def __del__(self): + if self.pid is not None: # only clean up in main process + self._removetemp() + + +class Result(object): + def __init__(self, exitstatus, signal, retval, stdout, stderr): + self.exitstatus = exitstatus + self.signal = signal + self.retval = retval + self.out = stdout + self.err = stderr diff --git a/venv/lib/python3.5/site-packages/py/_process/killproc.py b/venv/lib/python3.5/site-packages/py/_process/killproc.py new file mode 100644 index 0000000..5b6032c --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_process/killproc.py @@ -0,0 +1,23 @@ +import py +import os, sys + +if sys.platform == "win32" or getattr(os, '_name', '') == 'nt': + try: + import ctypes + except ImportError: + def dokill(pid): + py.process.cmdexec("taskkill /F /PID %d" %(pid,)) + else: + def dokill(pid): + PROCESS_TERMINATE = 1 + handle = ctypes.windll.kernel32.OpenProcess( + PROCESS_TERMINATE, False, pid) + ctypes.windll.kernel32.TerminateProcess(handle, -1) + ctypes.windll.kernel32.CloseHandle(handle) +else: + def dokill(pid): + os.kill(pid, 15) + +def kill(pid): + """ kill process by id. """ + dokill(pid) diff --git a/venv/lib/python3.5/site-packages/py/_std.py b/venv/lib/python3.5/site-packages/py/_std.py new file mode 100644 index 0000000..e016bc8 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_std.py @@ -0,0 +1,18 @@ +import sys + +class Std(object): + """ makes top-level python modules available as an attribute, + importing them on first access. + """ + + def __init__(self): + self.__dict__ = sys.modules + + def __getattr__(self, name): + try: + m = __import__(name) + except ImportError: + raise AttributeError("py.std: could not import %s" % name) + return m + +std = Std() diff --git a/venv/lib/python3.5/site-packages/py/_xmlgen.py b/venv/lib/python3.5/site-packages/py/_xmlgen.py new file mode 100644 index 0000000..f4d4116 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/_xmlgen.py @@ -0,0 +1,255 @@ +""" +module for generating and serializing xml and html structures +by using simple python objects. + +(c) holger krekel, holger at merlinux eu. 2009 +""" +import sys, re + +if sys.version_info >= (3,0): + def u(s): + return s + def unicode(x, errors=None): + if hasattr(x, '__unicode__'): + return x.__unicode__() + return str(x) +else: + def u(s): + return unicode(s) + unicode = unicode + + +class NamespaceMetaclass(type): + def __getattr__(self, name): + if name[:1] == '_': + raise AttributeError(name) + if self == Namespace: + raise ValueError("Namespace class is abstract") + tagspec = self.__tagspec__ + if tagspec is not None and name not in tagspec: + raise AttributeError(name) + classattr = {} + if self.__stickyname__: + classattr['xmlname'] = name + cls = type(name, (self.__tagclass__,), classattr) + setattr(self, name, cls) + return cls + +class Tag(list): + class Attr(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __init__(self, *args, **kwargs): + super(Tag, self).__init__(args) + self.attr = self.Attr(**kwargs) + + def __unicode__(self): + return self.unicode(indent=0) + __str__ = __unicode__ + + def unicode(self, indent=2): + l = [] + SimpleUnicodeVisitor(l.append, indent).visit(self) + return u("").join(l) + + def __repr__(self): + name = self.__class__.__name__ + return "<%r tag object %d>" % (name, id(self)) + +Namespace = NamespaceMetaclass('Namespace', (object, ), { + '__tagspec__': None, + '__tagclass__': Tag, + '__stickyname__': False, +}) + +class HtmlTag(Tag): + def unicode(self, indent=2): + l = [] + HtmlVisitor(l.append, indent, shortempty=False).visit(self) + return u("").join(l) + +# exported plain html namespace +class html(Namespace): + __tagclass__ = HtmlTag + __stickyname__ = True + __tagspec__ = dict([(x,1) for x in ( + 'a,abbr,acronym,address,applet,area,article,aside,audio,b,' + 'base,basefont,bdi,bdo,big,blink,blockquote,body,br,button,' + 'canvas,caption,center,cite,code,col,colgroup,command,comment,' + 'datalist,dd,del,details,dfn,dir,div,dl,dt,em,embed,' + 'fieldset,figcaption,figure,footer,font,form,frame,frameset,h1,' + 'h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,' + 'ins,isindex,kbd,keygen,label,legend,li,link,listing,map,mark,' + 'marquee,menu,meta,meter,multicol,nav,nobr,noembed,noframes,' + 'noscript,object,ol,optgroup,option,output,p,param,pre,progress,' + 'q,rp,rt,ruby,s,samp,script,section,select,small,source,span,' + 'strike,strong,style,sub,summary,sup,table,tbody,td,textarea,' + 'tfoot,th,thead,time,title,tr,track,tt,u,ul,xmp,var,video,wbr' + ).split(',') if x]) + + class Style(object): + def __init__(self, **kw): + for x, y in kw.items(): + x = x.replace('_', '-') + setattr(self, x, y) + + +class raw(object): + """just a box that can contain a unicode string that will be + included directly in the output""" + def __init__(self, uniobj): + self.uniobj = uniobj + +class SimpleUnicodeVisitor(object): + """ recursive visitor to write unicode. """ + def __init__(self, write, indent=0, curindent=0, shortempty=True): + self.write = write + self.cache = {} + self.visited = {} # for detection of recursion + self.indent = indent + self.curindent = curindent + self.parents = [] + self.shortempty = shortempty # short empty tags or not + + def visit(self, node): + """ dispatcher on node's class/bases name. """ + cls = node.__class__ + try: + visitmethod = self.cache[cls] + except KeyError: + for subclass in cls.__mro__: + visitmethod = getattr(self, subclass.__name__, None) + if visitmethod is not None: + break + else: + visitmethod = self.__object + self.cache[cls] = visitmethod + visitmethod(node) + + # the default fallback handler is marked private + # to avoid clashes with the tag name object + def __object(self, obj): + #self.write(obj) + self.write(escape(unicode(obj))) + + def raw(self, obj): + self.write(obj.uniobj) + + def list(self, obj): + assert id(obj) not in self.visited + self.visited[id(obj)] = 1 + for elem in obj: + self.visit(elem) + + def Tag(self, tag): + assert id(tag) not in self.visited + try: + tag.parent = self.parents[-1] + except IndexError: + tag.parent = None + self.visited[id(tag)] = 1 + tagname = getattr(tag, 'xmlname', tag.__class__.__name__) + if self.curindent and not self._isinline(tagname): + self.write("\n" + u(' ') * self.curindent) + if tag: + self.curindent += self.indent + self.write(u('<%s%s>') % (tagname, self.attributes(tag))) + self.parents.append(tag) + for x in tag: + self.visit(x) + self.parents.pop() + self.write(u('</%s>') % tagname) + self.curindent -= self.indent + else: + nameattr = tagname+self.attributes(tag) + if self._issingleton(tagname): + self.write(u('<%s/>') % (nameattr,)) + else: + self.write(u('<%s></%s>') % (nameattr, tagname)) + + def attributes(self, tag): + # serialize attributes + attrlist = dir(tag.attr) + attrlist.sort() + l = [] + for name in attrlist: + res = self.repr_attribute(tag.attr, name) + if res is not None: + l.append(res) + l.extend(self.getstyle(tag)) + return u("").join(l) + + def repr_attribute(self, attrs, name): + if name[:2] != '__': + value = getattr(attrs, name) + if name.endswith('_'): + name = name[:-1] + if isinstance(value, raw): + insert = value.uniobj + else: + insert = escape(unicode(value)) + return ' %s="%s"' % (name, insert) + + def getstyle(self, tag): + """ return attribute list suitable for styling. """ + try: + styledict = tag.style.__dict__ + except AttributeError: + return [] + else: + stylelist = [x+': ' + y for x,y in styledict.items()] + return [u(' style="%s"') % u('; ').join(stylelist)] + + def _issingleton(self, tagname): + """can (and will) be overridden in subclasses""" + return self.shortempty + + def _isinline(self, tagname): + """can (and will) be overridden in subclasses""" + return False + +class HtmlVisitor(SimpleUnicodeVisitor): + + single = dict([(x, 1) for x in + ('br,img,area,param,col,hr,meta,link,base,' + 'input,frame').split(',')]) + inline = dict([(x, 1) for x in + ('a abbr acronym b basefont bdo big br cite code dfn em font ' + 'i img input kbd label q s samp select small span strike ' + 'strong sub sup textarea tt u var'.split(' '))]) + + def repr_attribute(self, attrs, name): + if name == 'class_': + value = getattr(attrs, name) + if value is None: + return + return super(HtmlVisitor, self).repr_attribute(attrs, name) + + def _issingleton(self, tagname): + return tagname in self.single + + def _isinline(self, tagname): + return tagname in self.inline + + +class _escape: + def __init__(self): + self.escape = { + u('"') : u('"'), u('<') : u('<'), u('>') : u('>'), + u('&') : u('&'), u("'") : u('''), + } + self.charef_rex = re.compile(u("|").join(self.escape.keys())) + + def _replacer(self, match): + return self.escape[match.group(0)] + + def __call__(self, ustring): + """ xml-escape the given unicode string. """ + try: + ustring = unicode(ustring) + except UnicodeDecodeError: + ustring = unicode(ustring, 'utf-8', errors='replace') + return self.charef_rex.sub(self._replacer, ustring) + +escape = _escape() diff --git a/venv/lib/python3.5/site-packages/py/test.py b/venv/lib/python3.5/site-packages/py/test.py new file mode 100644 index 0000000..84e8881 --- /dev/null +++ b/venv/lib/python3.5/site-packages/py/test.py @@ -0,0 +1,10 @@ +import sys +if __name__ == '__main__': + import pytest + sys.exit(pytest.main()) +else: + import sys, pytest + sys.modules['py.test'] = pytest + +# for more API entry points see the 'tests' definition +# in __init__.py diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..387164f --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/DESCRIPTION.rst @@ -0,0 +1,111 @@ +.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png + :target: http://docs.pytest.org + :align: center + :alt: pytest + +------ + +.. image:: https://img.shields.io/pypi/v/pytest.svg + :target: https://pypi.python.org/pypi/pytest + +.. image:: https://anaconda.org/conda-forge/pytest/badges/version.svg + :target: https://anaconda.org/conda-forge/pytest + +.. image:: https://img.shields.io/pypi/pyversions/pytest.svg + :target: https://pypi.python.org/pypi/pytest + +.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg + :target: https://coveralls.io/r/pytest-dev/pytest + +.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master + :target: https://travis-ci.org/pytest-dev/pytest + +.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true + :target: https://ci.appveyor.com/project/pytestbot/pytest + +The ``pytest`` framework makes it easy to write small tests, yet +scales to support complex functional testing for applications and libraries. + +An example of a simple test: + +.. code-block:: python + + # content of test_sample.py + def inc(x): + return x + 1 + + def test_answer(): + assert inc(3) == 5 + + +To execute it:: + + $ pytest + ============================= test session starts ============================= + collected 1 items + + test_sample.py F + + ================================== FAILURES =================================== + _________________________________ test_answer _________________________________ + + def test_answer(): + > assert inc(3) == 5 + E assert 4 == 5 + E + where 4 = inc(3) + + test_sample.py:5: AssertionError + ========================== 1 failed in 0.04 seconds =========================== + + +Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples. + + +Features +-------- + +- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names); + +- `Auto-discovery + <http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_ + of test modules and functions; + +- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for + managing small or parametrized long-lived test resources; + +- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial), + `nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box; + +- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested); + +- Rich plugin architecture, with over 150+ `external plugins <http://docs.pytest.org/en/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community; + + +Documentation +------------- + +For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org. + + +Bugs/Requests +------------- + +Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features. + + +Changelog +--------- + +Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version. + + +License +------- + +Copyright Holger Krekel and others, 2004-2017. + +Distributed under the terms of the `MIT`_ license, pytest is free and open source software. + +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE + + diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/LICENSE.txt b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/LICENSE.txt new file mode 100644 index 0000000..629df45 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2004-2017 Holger Krekel and others + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/METADATA b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/METADATA new file mode 100644 index 0000000..bba1b96 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/METADATA @@ -0,0 +1,147 @@ +Metadata-Version: 2.0 +Name: pytest +Version: 3.1.3 +Summary: pytest: simple powerful testing with Python +Home-page: http://pytest.org +Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others +Author-email: UNKNOWN +License: MIT license +Keywords: test unittest +Platform: unix +Platform: linux +Platform: osx +Platform: cygwin +Platform: win32 +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Topic :: Software Development :: Testing +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Requires-Dist: py (>=1.4.33) +Requires-Dist: setuptools +Requires-Dist: argparse; python_version=="2.6" +Requires-Dist: colorama; sys_platform=="win32" + +.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png + :target: http://docs.pytest.org + :align: center + :alt: pytest + +------ + +.. image:: https://img.shields.io/pypi/v/pytest.svg + :target: https://pypi.python.org/pypi/pytest + +.. image:: https://anaconda.org/conda-forge/pytest/badges/version.svg + :target: https://anaconda.org/conda-forge/pytest + +.. image:: https://img.shields.io/pypi/pyversions/pytest.svg + :target: https://pypi.python.org/pypi/pytest + +.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg + :target: https://coveralls.io/r/pytest-dev/pytest + +.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master + :target: https://travis-ci.org/pytest-dev/pytest + +.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true + :target: https://ci.appveyor.com/project/pytestbot/pytest + +The ``pytest`` framework makes it easy to write small tests, yet +scales to support complex functional testing for applications and libraries. + +An example of a simple test: + +.. code-block:: python + + # content of test_sample.py + def inc(x): + return x + 1 + + def test_answer(): + assert inc(3) == 5 + + +To execute it:: + + $ pytest + ============================= test session starts ============================= + collected 1 items + + test_sample.py F + + ================================== FAILURES =================================== + _________________________________ test_answer _________________________________ + + def test_answer(): + > assert inc(3) == 5 + E assert 4 == 5 + E + where 4 = inc(3) + + test_sample.py:5: AssertionError + ========================== 1 failed in 0.04 seconds =========================== + + +Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples. + + +Features +-------- + +- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names); + +- `Auto-discovery + <http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_ + of test modules and functions; + +- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for + managing small or parametrized long-lived test resources; + +- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial), + `nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box; + +- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested); + +- Rich plugin architecture, with over 150+ `external plugins <http://docs.pytest.org/en/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community; + + +Documentation +------------- + +For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org. + + +Bugs/Requests +------------- + +Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features. + + +Changelog +--------- + +Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version. + + +License +------- + +Copyright Holger Krekel and others, 2004-2017. + +Distributed under the terms of the `MIT`_ license, pytest is free and open source software. + +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE + + diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/RECORD b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/RECORD new file mode 100644 index 0000000..fc41779 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/RECORD @@ -0,0 +1,99 @@ +pytest.py,sha256=h94kggJr6sOBzLs3f5I03sXbHcdkFvWujEYp1pRGKiU,1803 +_pytest/__init__.py,sha256=tnN4b4FVL2drauWlNtu0BWXVgHN0Zi9nuTVfj4rQ3VU,239 +_pytest/_argcomplete.py,sha256=wQLVQKsi2hyc1BC04GMwojLcNVbcK9V_pN6dANxBduw,3688 +_pytest/_pluggy.py,sha256=_RXK7wPtdCW9S9cnEOK_CSV0jJwkwYaIlXeN4nsmVy8,426 +_pytest/_version.py,sha256=WyMlz3XJ-2tbmAfcmKaMgYzg8GYvwRXgKjwswhsmd8M,116 +_pytest/cacheprovider.py,sha256=tKfmAZCvJgIJxqMPv4j5tZc2Ywwv0pVmbeg2wcRUzFQ,9009 +_pytest/capture.py,sha256=KAYlzq7q91FEqkAcpfJ3yqErwId-uYxArB2n2O2chHE,17102 +_pytest/compat.py,sha256=qAuK9I-DkGkvC8TmjC2oDS93E-Q9PZsupg8F-uOGNeo,9117 +_pytest/config.py,sha256=3jFfaiqXTFBiB9BQ69NOQrSxnbuzAa8okk55fqi8n7A,52819 +_pytest/debugging.py,sha256=k8kC5XkMcDYI7eBm9sN8fny28HWYGVc5cSu8uwUZkds,4000 +_pytest/deprecated.py,sha256=50pCgBIMjjppF9N0vB3oMi24q8f1zmqFIMUAnLQ05h8,1048 +_pytest/doctest.py,sha256=rWj3godu2EArE-AtaFwoYMkYhBOpyFt5PKBQJ9Hlzm4,12960 +_pytest/fixtures.py,sha256=d0x25P7YlnAeVwOQx9XNuQkc8m-J5UyLydJrhMwN_RA,45027 +_pytest/freeze_support.py,sha256=AUcO6JGsLgRmhd_pDT4ICJFyXVZCdtitK_yPNuii4OE,1195 +_pytest/helpconfig.py,sha256=mq5PVrAU_rTzU3FcqZ4AzQ0Wpqtuqfxy3Oqm0oEVoFk,6486 +_pytest/hookspec.py,sha256=V-d7guBoDCEiV03mbn-KHi7B0L7Ive4823PmAKIHcV0,13228 +_pytest/junitxml.py,sha256=8bmGyMb2wAvKUGVuf9_N9AmQJE1g_Y6P8oeMAkf-ypE,15667 +_pytest/main.py,sha256=9AHR7kYXkCvMqzPcYNIE9i8aG21MysBepZGoUbSYF2I,27960 +_pytest/mark.py,sha256=gvHylqTuCuAGdFBOfEsSdXXloTyB7kCv2wjtPcpXoFM,13118 +_pytest/monkeypatch.py,sha256=wucFrEdH6kD30PazY5AU6OvAVEkC-4c0RjmDT9ojWek,9051 +_pytest/nose.py,sha256=zFEuaNGz8zy0L8f8_xu9y_DUwmtPjEoaw0fLHLQRe1g,2616 +_pytest/pastebin.py,sha256=-GRAii-D3XAkfiCvUTEAqH6Wyjg7rtCO5vUW_bCNI7U,3555 +_pytest/pytester.py,sha256=2MsQhBtXnhrksSYj4dMV7ScsC4SKG9cSIsn1i5y2TXs,40057 +_pytest/python.py,sha256=WIEkcLeu44kcsAX_A553cGG1Gpy3PdUuiCigmyN-z9c,61662 +_pytest/recwarn.py,sha256=5W9cNVbI-4Vx0Mj3zEd67DOnLeoP4n_m71eoZjC0QaQ,6995 +_pytest/resultlog.py,sha256=6BzQwDqnOEhnreyDNAAM4FxJDYB6IsfFYMatR-oUOUs,3677 +_pytest/runner.py,sha256=fsg-52OuX9k-3wh_D6CtffzBZyLwncmXht__GvHp_UM,19302 +_pytest/setuponly.py,sha256=Oif-L4JQdNrB7RJxf6-gK9_ilYANbFO65U8yAS01kfY,2560 +_pytest/setupplan.py,sha256=RO4GbGY7CtfTzFLBF162INxg7RabhXrUTUrmAL-PFvc,816 +_pytest/skipping.py,sha256=dQtYNHRQgOj9R08aLjWiQZSqDjbCTnTYSUCD3fqOw9s,13618 +_pytest/terminal.py,sha256=tGh2OMX21zzobxtj1PPDWj7DoOcGfAiNAOVnMrekrWU,23432 +_pytest/tmpdir.py,sha256=XKkZ32soWaFhej4zGIRQD5Tlc9MHk1qoa_hzUThcpes,4191 +_pytest/unittest.py,sha256=OciNcH7KaGBBf3mplwOVLJ4y0o2PJUWt5gi8BZIainQ,8437 +_pytest/warnings.py,sha256=cqA6bM47p5u4pO6jObuugVXH2-wg4I95DpmqzDfsfCY,2921 +_pytest/_code/__init__.py,sha256=xc4AtbMH64T0uj93rM06vh-wVInRayAbXpmamDyWWiQ,410 +_pytest/_code/_py2traceback.py,sha256=8NDEHOVaSlxdhewqx7xSRhPpIVCIvN0kZGdWHP-U618,2952 +_pytest/_code/code.py,sha256=aHnAmRXxxaE8-Xy0pPCJyt3irMo764GOOXMRQzzwnoE,31740 +_pytest/_code/source.py,sha256=OJum5Udz9vGmkJYKj8_c9gqrd6g3nv_1SwqaDXmCYGM,14136 +_pytest/assertion/__init__.py,sha256=uipwI8Dd7RmJsBArfbEbyjVtVkYPvMrHrVaL6N8Q81A,5248 +_pytest/assertion/rewrite.py,sha256=PYhOoZsryIVSmNwhg9ufETViJ6e6NOU2RxoBvY5JV3c,36227 +_pytest/assertion/truncate.py,sha256=GD9M8y0ZDhTvy7wnQXy7wJHJFIu0mi8lQDy_JFjB65s,3367 +_pytest/assertion/util.py,sha256=AY88RujtEkw40qDBefA6sDlxUXJbk-TEQFtc1geVecU,10769 +_pytest/vendored_packages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +_pytest/vendored_packages/pluggy.py,sha256=yNWiA33HWoA5COc2__KdR1Oh7RRYFc22oETzYWZFFiw,30788 +pytest-3.1.3.dist-info/DESCRIPTION.rst,sha256=d9ltTXUsqAIufcSxb3WD7HvBUwXRtzI6eDgQHT9LmrM,3479 +pytest-3.1.3.dist-info/LICENSE.txt,sha256=oevOFa_Htc-Yx8beUS0ZWdS_YduMa_LxEShtSDtAqZc,1096 +pytest-3.1.3.dist-info/METADATA,sha256=O3KZOjsI4edrTiCR_q3Rt2pG_oMuXRA2O4aUuI2lxp4,4842 +pytest-3.1.3.dist-info/RECORD,, +pytest-3.1.3.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +pytest-3.1.3.dist-info/entry_points.txt,sha256=axdK_VT5P2oD8QJv5r7o8lo9DC45aJ2uvB3vpTP-qSE,62 +pytest-3.1.3.dist-info/metadata.json,sha256=FXVC1UUlZ8HVdh7GqS7X-hoRxmLiWE1HKUdGElxAq_E,1641 +pytest-3.1.3.dist-info/top_level.txt,sha256=ENE0IeZV1I1R61DOt8gs5KmSXwitaq2zstF0az5f9PA,15 +../../../bin/py.test,sha256=U2UBdj1zgvz3nzbf3YWXZ3ucDbEeuHwnRE2idKmN2iM,253 +../../../bin/pytest,sha256=U2UBdj1zgvz3nzbf3YWXZ3ucDbEeuHwnRE2idKmN2iM,253 +pytest-3.1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +_pytest/__pycache__/unittest.cpython-35.pyc,, +_pytest/__pycache__/recwarn.cpython-35.pyc,, +_pytest/__pycache__/_version.cpython-35.pyc,, +_pytest/__pycache__/main.cpython-35.pyc,, +_pytest/__pycache__/skipping.cpython-35.pyc,, +_pytest/__pycache__/mark.cpython-35.pyc,, +_pytest/assertion/__pycache__/truncate.cpython-35.pyc,, +_pytest/__pycache__/hookspec.cpython-35.pyc,, +__pycache__/pytest.cpython-35.pyc,, +_pytest/__pycache__/python.cpython-35.pyc,, +_pytest/assertion/__pycache__/__init__.cpython-35.pyc,, +_pytest/__pycache__/pastebin.cpython-35.pyc,, +_pytest/__pycache__/freeze_support.cpython-35.pyc,, +_pytest/__pycache__/setuponly.cpython-35.pyc,, +_pytest/__pycache__/debugging.cpython-35.pyc,, +_pytest/vendored_packages/__pycache__/__init__.cpython-35.pyc,, +_pytest/_code/__pycache__/source.cpython-35.pyc,, +_pytest/assertion/__pycache__/rewrite.cpython-35.pyc,, +_pytest/__pycache__/warnings.cpython-35.pyc,, +_pytest/__pycache__/runner.cpython-35.pyc,, +_pytest/__pycache__/resultlog.cpython-35.pyc,, +_pytest/__pycache__/tmpdir.cpython-35.pyc,, +_pytest/__pycache__/fixtures.cpython-35.pyc,, +_pytest/__pycache__/_pluggy.cpython-35.pyc,, +_pytest/_code/__pycache__/__init__.cpython-35.pyc,, +_pytest/__pycache__/capture.cpython-35.pyc,, +_pytest/__pycache__/compat.cpython-35.pyc,, +_pytest/_code/__pycache__/code.cpython-35.pyc,, +_pytest/__pycache__/doctest.cpython-35.pyc,, +_pytest/vendored_packages/__pycache__/pluggy.cpython-35.pyc,, +_pytest/_code/__pycache__/_py2traceback.cpython-35.pyc,, +_pytest/__pycache__/helpconfig.cpython-35.pyc,, +_pytest/__pycache__/pytester.cpython-35.pyc,, +_pytest/__pycache__/setupplan.cpython-35.pyc,, +_pytest/__pycache__/_argcomplete.cpython-35.pyc,, +_pytest/__pycache__/__init__.cpython-35.pyc,, +_pytest/__pycache__/cacheprovider.cpython-35.pyc,, +_pytest/__pycache__/terminal.cpython-35.pyc,, +_pytest/__pycache__/monkeypatch.cpython-35.pyc,, +_pytest/__pycache__/deprecated.cpython-35.pyc,, +_pytest/__pycache__/config.cpython-35.pyc,, +_pytest/assertion/__pycache__/util.cpython-35.pyc,, +_pytest/__pycache__/junitxml.cpython-35.pyc,, +_pytest/__pycache__/nose.cpython-35.pyc,, diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/WHEEL b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/WHEEL new file mode 100644 index 0000000..8b6dd1b --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/entry_points.txt new file mode 100644 index 0000000..d8e4fd2 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +py.test = pytest:main +pytest = pytest:main + diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/metadata.json b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/metadata.json new file mode 100644 index 0000000..c58d0b0 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6"], "extensions": {"python.commands": {"wrap_console": {"py.test": "pytest:main", "pytest": "pytest:main"}}, "python.details": {"contacts": [{"name": "Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "http://pytest.org"}}, "python.exports": {"console_scripts": {"py.test": "pytest:main", "pytest": "pytest:main"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "keywords": ["test", "unittest"], "license": "MIT license", "metadata_version": "2.0", "name": "pytest", "platform": "unix", "run_requires": [{"requires": ["py (>=1.4.33)", "setuptools"]}, {"environment": "python_version==\"2.6\"", "requires": ["argparse"]}, {"environment": "sys_platform==\"win32\"", "requires": ["colorama"]}], "summary": "pytest: simple powerful testing with Python", "version": "3.1.3"} \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/top_level.txt new file mode 100644 index 0000000..e94857a --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest-3.1.3.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_pytest +pytest diff --git a/venv/lib/python3.5/site-packages/pytest.py b/venv/lib/python3.5/site-packages/pytest.py new file mode 100644 index 0000000..4e4ccb3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/pytest.py @@ -0,0 +1,78 @@ +# PYTHON_ARGCOMPLETE_OK +""" +pytest: unit and functional testing with Python. +""" + + +# else we are imported + +from _pytest.config import ( + main, UsageError, _preloadplugins, cmdline, + hookspec, hookimpl +) +from _pytest.fixtures import fixture, yield_fixture +from _pytest.assertion import register_assert_rewrite +from _pytest.freeze_support import freeze_includes +from _pytest import __version__ +from _pytest.debugging import pytestPDB as __pytestPDB +from _pytest.recwarn import warns, deprecated_call +from _pytest.runner import fail, skip, importorskip, exit +from _pytest.mark import MARK_GEN as mark, param +from _pytest.skipping import xfail +from _pytest.main import Item, Collector, File, Session +from _pytest.fixtures import fillfixtures as _fillfuncargs +from _pytest.python import ( + raises, approx, + Module, Class, Instance, Function, Generator, +) + +set_trace = __pytestPDB.set_trace + +__all__ = [ + 'main', + 'UsageError', + 'cmdline', + 'hookspec', + 'hookimpl', + '__version__', + 'register_assert_rewrite', + 'freeze_includes', + 'set_trace', + 'warns', + 'deprecated_call', + 'fixture', + 'yield_fixture', + 'fail', + 'skip', + 'xfail', + 'importorskip', + 'exit', + 'mark', + 'param', + 'approx', + '_fillfuncargs', + + 'Item', + 'File', + 'Collector', + 'Session', + 'Module', + 'Class', + 'Instance', + 'Function', + 'Generator', + 'raises', + + +] + +if __name__ == '__main__': + # if run as a script or by 'python -m pytest' + # we trigger the below "else" condition by the following import + import pytest + raise SystemExit(pytest.main()) +else: + + from _pytest.compat import _setup_collect_fakemodule + _preloadplugins() # to populate pytest.* namespace so help(pytest) works + _setup_collect_fakemodule() diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/DESCRIPTION.rst b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..15e8d25 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,238 @@ +=============================== +Installing and Using Setuptools +=============================== + +.. contents:: **Table of Contents** + + +`Change History <https://pythonhosted.org/setuptools/history.html>`_. + +------------------------- +Installation Instructions +------------------------- + +The recommended way to bootstrap setuptools on any system is to download +`ez_setup.py`_ and run it using the target Python environment. Different +operating systems have different recommended techniques to accomplish this +basic routine, so below are some examples to get you started. + +Setuptools requires Python 2.6 or later. To install setuptools +on Python 2.4 or Python 2.5, use the `bootstrap script for Setuptools 1.x +<https://raw.githubusercontent.com/pypa/setuptools/bootstrap-py24/ez_setup.py>`_. + +The link provided to ez_setup.py is a bookmark to bootstrap script for the +latest known stable release. + +.. _ez_setup.py: https://bootstrap.pypa.io/ez_setup.py + +Windows (Powershell 3 or later) +=============================== + +For best results, uninstall previous versions FIRST (see `Uninstalling`_). + +Using Windows 8 (which includes PowerShell 3) or earlier versions of Windows +with PowerShell 3 installed, it's possible to install with one simple +Powershell command. Start up Powershell and paste this command:: + + > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - + +You must start the Powershell with Administrative privileges or you may choose +to install a user-local installation:: + + > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - --user + +If you have Python 3.3 or later, you can use the ``py`` command to install to +different Python versions. For example, to install to Python 3.3 if you have +Python 2.7 installed:: + + > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | py -3 - + +The recommended way to install setuptools on Windows is to download +`ez_setup.py`_ and run it. The script will download the appropriate +distribution file and install it for you. + +Once installation is complete, you will find an ``easy_install`` program in +your Python ``Scripts`` subdirectory. For simple invocation and best results, +add this directory to your ``PATH`` environment variable, if it is not already +present. If you did a user-local install, the ``Scripts`` subdirectory is +``$env:APPDATA\Python\Scripts``. + + +Windows (simplified) +==================== + +For Windows without PowerShell 3 or for installation without a command-line, +download `ez_setup.py`_ using your preferred web browser or other technique +and "run" that file. + + +Unix (wget) +=========== + +Most Linux distributions come with wget. + +Download `ez_setup.py`_ and run it using the target Python version. The script +will download the appropriate version and install it for you:: + + > wget https://bootstrap.pypa.io/ez_setup.py -O - | python + +Note that you will may need to invoke the command with superuser privileges to +install to the system Python:: + + > wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python + +Alternatively, Setuptools may be installed to a user-local path:: + + > wget https://bootstrap.pypa.io/ez_setup.py -O - | python - --user + +Note that on some older systems (noted on Debian 6 and CentOS 5 installations), +`wget` may refuse to download `ez_setup.py`, complaining that the certificate common name `*.c.ssl.fastly.net` +does not match the host name `bootstrap.pypa.io`. In addition, the `ez_setup.py` script may then encounter similar problems using +`wget` internally to download `setuptools-x.y.zip`, complaining that the certificate common name of `www.python.org` does not match the +host name `pypi.python.org`. Those are known issues, related to a bug in the older versions of `wget` +(see `Issue 59 <https://bitbucket.org/pypa/pypi/issue/59#comment-5881915>`_). If you happen to encounter them, +install Setuptools as follows:: + + > wget --no-check-certificate https://bootstrap.pypa.io/ez_setup.py + > python ez_setup.py --insecure + + +Unix including Mac OS X (curl) +============================== + +If your system has curl installed, follow the ``wget`` instructions but +replace ``wget`` with ``curl`` and ``-O`` with ``-o``. For example:: + + > curl https://bootstrap.pypa.io/ez_setup.py -o - | python + + +Advanced Installation +===================== + +For more advanced installation options, such as installing to custom +locations or prefixes, download and extract the source +tarball from `Setuptools on PyPI <https://pypi.python.org/pypi/setuptools>`_ +and run setup.py with any supported distutils and Setuptools options. +For example:: + + setuptools-x.x$ python setup.py install --prefix=/opt/setuptools + +Use ``--help`` to get a full options list, but we recommend consulting +the `EasyInstall manual`_ for detailed instructions, especially `the section +on custom installation locations`_. + +.. _EasyInstall manual: https://pythonhosted.org/setuptools/EasyInstall +.. _the section on custom installation locations: https://pythonhosted.org/setuptools/EasyInstall#custom-installation-locations + + +Downloads +========= + +All setuptools downloads can be found at `the project's home page in the Python +Package Index`_. Scroll to the very bottom of the page to find the links. + +.. _the project's home page in the Python Package Index: https://pypi.python.org/pypi/setuptools + +In addition to the PyPI downloads, the development version of ``setuptools`` +is available from the `Bitbucket repo`_, and in-development versions of the +`0.6 branch`_ are available as well. + +.. _Bitbucket repo: https://bitbucket.org/pypa/setuptools/get/default.tar.gz#egg=setuptools-dev +.. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06 + +Uninstalling +============ + +On Windows, if Setuptools was installed using an ``.exe`` or ``.msi`` +installer, simply use the uninstall feature of "Add/Remove Programs" in the +Control Panel. + +Otherwise, to uninstall Setuptools or Distribute, regardless of the Python +version, delete all ``setuptools*`` and ``distribute*`` files and +directories from your system's ``site-packages`` directory +(and any other ``sys.path`` directories) FIRST. + +If you are upgrading or otherwise plan to re-install Setuptools or Distribute, +nothing further needs to be done. If you want to completely remove Setuptools, +you may also want to remove the 'easy_install' and 'easy_install-x.x' scripts +and associated executables installed to the Python scripts directory. + +-------------------------------- +Using Setuptools and EasyInstall +-------------------------------- + +Here are some of the available manuals, tutorials, and other resources for +learning about Setuptools, Python Eggs, and EasyInstall: + +* `The EasyInstall user's guide and reference manual`_ +* `The setuptools Developer's Guide`_ +* `The pkg_resources API reference`_ +* `The Internal Structure of Python Eggs`_ + +Questions, comments, and bug reports should be directed to the `distutils-sig +mailing list`_. If you have written (or know of) any tutorials, documentation, +plug-ins, or other resources for setuptools users, please let us know about +them there, so this reference list can be updated. If you have working, +*tested* patches to correct problems or add features, you may submit them to +the `setuptools bug tracker`_. + +.. _setuptools bug tracker: https://github.com/pypa/setuptools/issues +.. _The Internal Structure of Python Eggs: https://pythonhosted.org/setuptools/formats.html +.. _The setuptools Developer's Guide: https://pythonhosted.org/setuptools/setuptools.html +.. _The pkg_resources API reference: https://pythonhosted.org/setuptools/pkg_resources.html +.. _The EasyInstall user's guide and reference manual: https://pythonhosted.org/setuptools/easy_install.html +.. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/ + + +------- +Credits +------- + +* The original design for the ``.egg`` format and the ``pkg_resources`` API was + co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first + version of ``pkg_resources``, and supplied the OS X operating system version + compatibility algorithm. + +* Ian Bicking implemented many early "creature comfort" features of + easy_install, including support for downloading via Sourceforge and + Subversion repositories. Ian's comments on the Web-SIG about WSGI + application deployment also inspired the concept of "entry points" in eggs, + and he has given talks at PyCon and elsewhere to inform and educate the + community about eggs and setuptools. + +* Jim Fulton contributed time and effort to build automated tests of various + aspects of ``easy_install``, and supplied the doctests for the command-line + ``.exe`` wrappers on Windows. + +* Phillip J. Eby is the seminal author of setuptools, and + first proposed the idea of an importable binary distribution format for + Python application plug-ins. + +* Significant parts of the implementation of setuptools were funded by the Open + Source Applications Foundation, to provide a plug-in infrastructure for the + Chandler PIM application. In addition, many OSAF staffers (such as Mike + "Code Bear" Taylor) contributed their time and stress as guinea pigs for the + use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!) + +* Tarek Ziadé is the principal author of the Distribute fork, which + re-invigorated the community on the project, encouraged renewed innovation, + and addressed many defects. + +* Since the merge with Distribute, Jason R. Coombs is the + maintainer of setuptools. The project is maintained in coordination with + the Python Packaging Authority (PyPA) and the larger Python community. + +.. _files: + + +--------------- +Code of Conduct +--------------- + +Everyone interacting in the setuptools project's codebases, issue trackers, +chat rooms, and mailing lists is expected to follow the +`PyPA Code of Conduct`_. + +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/INSTALLER b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/METADATA b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/METADATA new file mode 100644 index 0000000..de4691c --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/METADATA @@ -0,0 +1,263 @@ +Metadata-Version: 2.0 +Name: setuptools +Version: 20.7.0 +Summary: Easily download, build, install, upgrade, and uninstall Python packages +Home-page: https://github.com/pypa/setuptools +Author: Python Packaging Authority +Author-email: distutils-sig@python.org +License: UNKNOWN +Keywords: CPAN PyPI distutils eggs package management +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities + +=============================== +Installing and Using Setuptools +=============================== + +.. contents:: **Table of Contents** + + +`Change History <https://pythonhosted.org/setuptools/history.html>`_. + +------------------------- +Installation Instructions +------------------------- + +The recommended way to bootstrap setuptools on any system is to download +`ez_setup.py`_ and run it using the target Python environment. Different +operating systems have different recommended techniques to accomplish this +basic routine, so below are some examples to get you started. + +Setuptools requires Python 2.6 or later. To install setuptools +on Python 2.4 or Python 2.5, use the `bootstrap script for Setuptools 1.x +<https://raw.githubusercontent.com/pypa/setuptools/bootstrap-py24/ez_setup.py>`_. + +The link provided to ez_setup.py is a bookmark to bootstrap script for the +latest known stable release. + +.. _ez_setup.py: https://bootstrap.pypa.io/ez_setup.py + +Windows (Powershell 3 or later) +=============================== + +For best results, uninstall previous versions FIRST (see `Uninstalling`_). + +Using Windows 8 (which includes PowerShell 3) or earlier versions of Windows +with PowerShell 3 installed, it's possible to install with one simple +Powershell command. Start up Powershell and paste this command:: + + > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - + +You must start the Powershell with Administrative privileges or you may choose +to install a user-local installation:: + + > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - --user + +If you have Python 3.3 or later, you can use the ``py`` command to install to +different Python versions. For example, to install to Python 3.3 if you have +Python 2.7 installed:: + + > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | py -3 - + +The recommended way to install setuptools on Windows is to download +`ez_setup.py`_ and run it. The script will download the appropriate +distribution file and install it for you. + +Once installation is complete, you will find an ``easy_install`` program in +your Python ``Scripts`` subdirectory. For simple invocation and best results, +add this directory to your ``PATH`` environment variable, if it is not already +present. If you did a user-local install, the ``Scripts`` subdirectory is +``$env:APPDATA\Python\Scripts``. + + +Windows (simplified) +==================== + +For Windows without PowerShell 3 or for installation without a command-line, +download `ez_setup.py`_ using your preferred web browser or other technique +and "run" that file. + + +Unix (wget) +=========== + +Most Linux distributions come with wget. + +Download `ez_setup.py`_ and run it using the target Python version. The script +will download the appropriate version and install it for you:: + + > wget https://bootstrap.pypa.io/ez_setup.py -O - | python + +Note that you will may need to invoke the command with superuser privileges to +install to the system Python:: + + > wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python + +Alternatively, Setuptools may be installed to a user-local path:: + + > wget https://bootstrap.pypa.io/ez_setup.py -O - | python - --user + +Note that on some older systems (noted on Debian 6 and CentOS 5 installations), +`wget` may refuse to download `ez_setup.py`, complaining that the certificate common name `*.c.ssl.fastly.net` +does not match the host name `bootstrap.pypa.io`. In addition, the `ez_setup.py` script may then encounter similar problems using +`wget` internally to download `setuptools-x.y.zip`, complaining that the certificate common name of `www.python.org` does not match the +host name `pypi.python.org`. Those are known issues, related to a bug in the older versions of `wget` +(see `Issue 59 <https://bitbucket.org/pypa/pypi/issue/59#comment-5881915>`_). If you happen to encounter them, +install Setuptools as follows:: + + > wget --no-check-certificate https://bootstrap.pypa.io/ez_setup.py + > python ez_setup.py --insecure + + +Unix including Mac OS X (curl) +============================== + +If your system has curl installed, follow the ``wget`` instructions but +replace ``wget`` with ``curl`` and ``-O`` with ``-o``. For example:: + + > curl https://bootstrap.pypa.io/ez_setup.py -o - | python + + +Advanced Installation +===================== + +For more advanced installation options, such as installing to custom +locations or prefixes, download and extract the source +tarball from `Setuptools on PyPI <https://pypi.python.org/pypi/setuptools>`_ +and run setup.py with any supported distutils and Setuptools options. +For example:: + + setuptools-x.x$ python setup.py install --prefix=/opt/setuptools + +Use ``--help`` to get a full options list, but we recommend consulting +the `EasyInstall manual`_ for detailed instructions, especially `the section +on custom installation locations`_. + +.. _EasyInstall manual: https://pythonhosted.org/setuptools/EasyInstall +.. _the section on custom installation locations: https://pythonhosted.org/setuptools/EasyInstall#custom-installation-locations + + +Downloads +========= + +All setuptools downloads can be found at `the project's home page in the Python +Package Index`_. Scroll to the very bottom of the page to find the links. + +.. _the project's home page in the Python Package Index: https://pypi.python.org/pypi/setuptools + +In addition to the PyPI downloads, the development version of ``setuptools`` +is available from the `Bitbucket repo`_, and in-development versions of the +`0.6 branch`_ are available as well. + +.. _Bitbucket repo: https://bitbucket.org/pypa/setuptools/get/default.tar.gz#egg=setuptools-dev +.. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06 + +Uninstalling +============ + +On Windows, if Setuptools was installed using an ``.exe`` or ``.msi`` +installer, simply use the uninstall feature of "Add/Remove Programs" in the +Control Panel. + +Otherwise, to uninstall Setuptools or Distribute, regardless of the Python +version, delete all ``setuptools*`` and ``distribute*`` files and +directories from your system's ``site-packages`` directory +(and any other ``sys.path`` directories) FIRST. + +If you are upgrading or otherwise plan to re-install Setuptools or Distribute, +nothing further needs to be done. If you want to completely remove Setuptools, +you may also want to remove the 'easy_install' and 'easy_install-x.x' scripts +and associated executables installed to the Python scripts directory. + +-------------------------------- +Using Setuptools and EasyInstall +-------------------------------- + +Here are some of the available manuals, tutorials, and other resources for +learning about Setuptools, Python Eggs, and EasyInstall: + +* `The EasyInstall user's guide and reference manual`_ +* `The setuptools Developer's Guide`_ +* `The pkg_resources API reference`_ +* `The Internal Structure of Python Eggs`_ + +Questions, comments, and bug reports should be directed to the `distutils-sig +mailing list`_. If you have written (or know of) any tutorials, documentation, +plug-ins, or other resources for setuptools users, please let us know about +them there, so this reference list can be updated. If you have working, +*tested* patches to correct problems or add features, you may submit them to +the `setuptools bug tracker`_. + +.. _setuptools bug tracker: https://github.com/pypa/setuptools/issues +.. _The Internal Structure of Python Eggs: https://pythonhosted.org/setuptools/formats.html +.. _The setuptools Developer's Guide: https://pythonhosted.org/setuptools/setuptools.html +.. _The pkg_resources API reference: https://pythonhosted.org/setuptools/pkg_resources.html +.. _The EasyInstall user's guide and reference manual: https://pythonhosted.org/setuptools/easy_install.html +.. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/ + + +------- +Credits +------- + +* The original design for the ``.egg`` format and the ``pkg_resources`` API was + co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first + version of ``pkg_resources``, and supplied the OS X operating system version + compatibility algorithm. + +* Ian Bicking implemented many early "creature comfort" features of + easy_install, including support for downloading via Sourceforge and + Subversion repositories. Ian's comments on the Web-SIG about WSGI + application deployment also inspired the concept of "entry points" in eggs, + and he has given talks at PyCon and elsewhere to inform and educate the + community about eggs and setuptools. + +* Jim Fulton contributed time and effort to build automated tests of various + aspects of ``easy_install``, and supplied the doctests for the command-line + ``.exe`` wrappers on Windows. + +* Phillip J. Eby is the seminal author of setuptools, and + first proposed the idea of an importable binary distribution format for + Python application plug-ins. + +* Significant parts of the implementation of setuptools were funded by the Open + Source Applications Foundation, to provide a plug-in infrastructure for the + Chandler PIM application. In addition, many OSAF staffers (such as Mike + "Code Bear" Taylor) contributed their time and stress as guinea pigs for the + use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!) + +* Tarek Ziadé is the principal author of the Distribute fork, which + re-invigorated the community on the project, encouraged renewed innovation, + and addressed many defects. + +* Since the merge with Distribute, Jason R. Coombs is the + maintainer of setuptools. The project is maintained in coordination with + the Python Packaging Authority (PyPA) and the larger Python community. + +.. _files: + + +--------------- +Code of Conduct +--------------- + +Everyone interacting in the setuptools project's codebases, issue trackers, +chat rooms, and mailing lists is expected to follow the +`PyPA Code of Conduct`_. + +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/RECORD b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/RECORD new file mode 100644 index 0000000..833cb10 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/RECORD @@ -0,0 +1,100 @@ +easy_install.py,sha256=MDC9vt5AxDsXX5qcKlBz2TnW6Tpuv_AobnfhCJ9X3PM,126 +setuptools/__init__.py,sha256=WEGb6BRGN2dz3eJTbNRUfInUAhb6_OZJyYAndPGJm6w,5440 +setuptools/archive_util.py,sha256=N30WE5ZQjkytzhAodAXw4FkK-9J5AP1ChrClHnZthOA,6609 +setuptools/depends.py,sha256=WyJIhjIX7D5-JpGSnMAPHEoDcVPQxaO0405keTQT6jM,6418 +setuptools/dist.py,sha256=liJ6Ob7BoAdC8IkpomV08kFBeVD3B9f7hE-XCHTwbMw,35704 +setuptools/extension.py,sha256=YvsyGHWVWzhNOXMHU239FR14wxw2WwdMLLzWsRP6_IY,1694 +setuptools/launch.py,sha256=hP3qZxDNu5Hf9C-VAkEP4IC_YYfR1XfxMTj6EguxxCg,730 +setuptools/lib2to3_ex.py,sha256=6jPF9sJuHiz0cyg4cwIBLl2VMAxcl3GYSZwWAOuJplU,1998 +setuptools/msvc9_support.py,sha256=ALzUH_kzYcMFOsj8r0t275PEYouMUOrrZO6wXSSdoKs,2185 +setuptools/package_index.py,sha256=T6tZGPHApup6Gl3kz1sCLtY7kmMUXLBKweSAORYS2Qc,39490 +setuptools/py26compat.py,sha256=1Vvuf-hj5bTM3OAXv6vgJQImulne12ann053caOgikU,481 +setuptools/py27compat.py,sha256=CGj-jZcFgHUkrEdLvArkxHj96tAaMbG2-yJtUVU7QVI,306 +setuptools/py31compat.py,sha256=cqYSVBd2pxvKl75185z40htfEr6EKC29KvSBiSoqHOA,1636 +setuptools/sandbox.py,sha256=tuMRu_8R0_w6Qer9VqDiOTqKy1qr_GjHi-2QAg7TMz0,14210 +setuptools/script (dev).tmpl,sha256=f7MR17dTkzaqkCMSVseyOCMVrPVSMdmTQsaB8cZzfuI,201 +setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 +setuptools/site-patch.py,sha256=K-0-cAx36mX_PG-qPZwosG9ZLCliRjquKQ4nHiJvvzg,2389 +setuptools/ssl_support.py,sha256=tAFeeyFPVle_GgarPkNrdfnCJgP9PyN_QYGXTgypoyc,8119 +setuptools/unicode_utils.py,sha256=8zVyrL_MFc6P5AmErs21rr7z-3N1pZ_NkOcDC7BPElU,995 +setuptools/utils.py,sha256=08Z7mt-9mvrx-XvmS5EyKoRn2lxNTlgFsUwBU3Eq9JQ,293 +setuptools/version.py,sha256=khyqulaxZr_XgWpQpVu3JKz9h0H0KFPVcKajnhTfCxc,132 +setuptools/windows_support.py,sha256=5GrfqSP2-dLGJoZTq2g6dCKkyQxxa2n5IQiXlJCoYEE,714 +setuptools/command/__init__.py,sha256=1AM3hv_zCixE7kTXA-onWfK_2KF8GC8fUw3WSxzi5Fg,564 +setuptools/command/alias.py,sha256=KjpE0sz_SDIHv3fpZcIQK-sCkJz-SrC6Gmug6b9Nkc8,2426 +setuptools/command/bdist_egg.py,sha256=Km4CsGbevhvej6kKEfvTYxfkPoQijUyXmImNifrO4Tg,17184 +setuptools/command/bdist_rpm.py,sha256=B7l0TnzCGb-0nLlm6rS00jWLkojASwVmdhW2w5Qz_Ak,1508 +setuptools/command/bdist_wininst.py,sha256=_6dz3lpB1tY200LxKPLM7qgwTCceOMgaWFF-jW2-pm0,637 +setuptools/command/build_ext.py,sha256=pkQ8xp3YPVGGLkGv-SvfxC_GqFpboph1AFEoMFOgQMo,11964 +setuptools/command/build_py.py,sha256=HvJ88JuougDccaowYlfMV12kYtd0GLahg2DR2vQRqL4,7983 +setuptools/command/develop.py,sha256=VxSYbpM2jQqtRBn5klIjPVBo3sWKNZMlSbHHiRLUlZo,7383 +setuptools/command/easy_install.py,sha256=jqLpcIhP5z1dXaoL5y1NS0074b8VdtMtH8RWKlYZu04,88268 +setuptools/command/egg_info.py,sha256=0_8eI8hgLAlGt8Xk5kiodY_d9lxG6_RSescJISKBJgA,16890 +setuptools/command/install.py,sha256=a0EZpL_A866KEdhicTGbuyD_TYl1sykfzdrri-zazT4,4683 +setuptools/command/install_egg_info.py,sha256=fEqU1EplTs_vUjAzwiEB7LrtdZBQ3BefwuUZLZBDEQ0,5027 +setuptools/command/install_lib.py,sha256=5IZM251t4DzOdZAXCezdROr3X0SeeE41eyV059RNgZ4,5011 +setuptools/command/install_scripts.py,sha256=vX2JC6v7l090N7CrTfihWBklNbPvfNKAY2LRtukM9XE,2231 +setuptools/command/register.py,sha256=bHlMm1qmBbSdahTOT8w6UhA-EgeQIz7p6cD-qOauaiI,270 +setuptools/command/rotate.py,sha256=QGZS2t4CmBl7t79KQijNCjRMU50lu3nRhu4FXWB5LIE,2038 +setuptools/command/saveopts.py,sha256=za7QCBcQimKKriWcoCcbhxPjUz30gSB74zuTL47xpP4,658 +setuptools/command/sdist.py,sha256=kQetnPMw6ao3nurWGJZgS4HkOH4AknzMOSvqbVA6jGA,7050 +setuptools/command/setopt.py,sha256=cygJaJWJmiVhR0e_Uh_0_fWyCxMJIqK-Bu6K0LyYUtU,5086 +setuptools/command/test.py,sha256=N2f5RwxkjwU3YQzFYHtzHr636-pdX9XJDuPg5Y92kSo,6888 +setuptools/command/upload.py,sha256=OjAryq4ZoARZiaTN_MpuG1X8Pu9CJNCKmmbMg-gab5I,649 +setuptools/command/upload_docs.py,sha256=htXpASci5gKP0RIrGZRRmbll7RnTRuwvKWZkYsBlDMM,6815 +setuptools/extern/__init__.py,sha256=mTrrj4yLMdFeEwwnqKnSuvZM5RM-HPZ1iXLgaYDlB9o,132 +setuptools-20.7.0.dist-info/DESCRIPTION.rst,sha256=s4bDJYqySAMbCdv6Fqr4vEUMG_vRSpGo0N0z5Im_1UM,9945 +setuptools-20.7.0.dist-info/METADATA,sha256=ZjaJZJLbxKSGaCMoLPHs2OApsNeqCk8vgnibr2sCPro,10999 +setuptools-20.7.0.dist-info/RECORD,, +setuptools-20.7.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +setuptools-20.7.0.dist-info/dependency_links.txt,sha256=oUNXJEArClXFiSSvfFwUKY8TYjeIXhuFfCpXn5K0DCE,226 +setuptools-20.7.0.dist-info/entry_points.txt,sha256=S6yRfyEABPIKq4cNMNO_7LHXzFVZW-exLSrKSI6kgNU,2779 +setuptools-20.7.0.dist-info/metadata.json,sha256=owxzpSorzMYFIWuy_rmAOseCtAdF59Lf98DD9h-lenw,4239 +setuptools-20.7.0.dist-info/top_level.txt,sha256=2HUXVVwA4Pff1xgTFr3GsTXXKaPaO6vlG6oNJ_4u4Tg,38 +setuptools-20.7.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +../../../bin/easy_install,sha256=hx8dJQDFewDm3i-k0hClfOY_VX3MMPQfFb_cSgEWJgs,278 +../../../bin/easy_install-3.5,sha256=hx8dJQDFewDm3i-k0hClfOY_VX3MMPQfFb_cSgEWJgs,278 +setuptools-20.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +setuptools/__pycache__/msvc9_support.cpython-35.pyc,, +setuptools/__pycache__/windows_support.cpython-35.pyc,, +setuptools/command/__pycache__/easy_install.cpython-35.pyc,, +setuptools/command/__pycache__/bdist_wininst.cpython-35.pyc,, +setuptools/command/__pycache__/bdist_egg.cpython-35.pyc,, +setuptools/__pycache__/archive_util.cpython-35.pyc,, +setuptools/command/__pycache__/install.cpython-35.pyc,, +setuptools/command/__pycache__/upload.cpython-35.pyc,, +__pycache__/easy_install.cpython-35.pyc,, +setuptools/command/__pycache__/develop.cpython-35.pyc,, +setuptools/__pycache__/sandbox.cpython-35.pyc,, +setuptools/command/__pycache__/egg_info.cpython-35.pyc,, +setuptools/__pycache__/py27compat.cpython-35.pyc,, +setuptools/__pycache__/launch.cpython-35.pyc,, +setuptools/__pycache__/ssl_support.cpython-35.pyc,, +setuptools/__pycache__/lib2to3_ex.cpython-35.pyc,, +setuptools/command/__pycache__/sdist.cpython-35.pyc,, +setuptools/command/__pycache__/alias.cpython-35.pyc,, +setuptools/__pycache__/package_index.cpython-35.pyc,, +setuptools/command/__pycache__/build_py.cpython-35.pyc,, +setuptools/__pycache__/__init__.cpython-35.pyc,, +setuptools/__pycache__/version.cpython-35.pyc,, +setuptools/command/__pycache__/build_ext.cpython-35.pyc,, +setuptools/command/__pycache__/upload_docs.cpython-35.pyc,, +setuptools/command/__pycache__/saveopts.cpython-35.pyc,, +setuptools/__pycache__/py26compat.cpython-35.pyc,, +setuptools/command/__pycache__/install_lib.cpython-35.pyc,, +setuptools/__pycache__/extension.cpython-35.pyc,, +setuptools/command/__pycache__/rotate.cpython-35.pyc,, +setuptools/__pycache__/utils.cpython-35.pyc,, +setuptools/command/__pycache__/bdist_rpm.cpython-35.pyc,, +setuptools/__pycache__/site-patch.cpython-35.pyc,, +setuptools/command/__pycache__/register.cpython-35.pyc,, +setuptools/command/__pycache__/install_egg_info.cpython-35.pyc,, +setuptools/extern/__pycache__/__init__.cpython-35.pyc,, +setuptools/__pycache__/depends.cpython-35.pyc,, +setuptools/__pycache__/unicode_utils.cpython-35.pyc,, +setuptools/command/__pycache__/test.cpython-35.pyc,, +setuptools/command/__pycache__/install_scripts.cpython-35.pyc,, +setuptools/command/__pycache__/setopt.cpython-35.pyc,, +setuptools/__pycache__/dist.cpython-35.pyc,, +setuptools/command/__pycache__/__init__.cpython-35.pyc,, +setuptools/__pycache__/py31compat.cpython-35.pyc,, diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/WHEEL b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/WHEEL new file mode 100644 index 0000000..8b6dd1b --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/dependency_links.txt b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/dependency_links.txt new file mode 100644 index 0000000..47d1e81 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/dependency_links.txt @@ -0,0 +1,2 @@ +https://pypi.python.org/packages/source/c/certifi/certifi-2015.11.20.tar.gz#md5=25134646672c695c1ff1593c2dd75d08 +https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2 diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/entry_points.txt b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/entry_points.txt new file mode 100644 index 0000000..5270e4a --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/entry_points.txt @@ -0,0 +1,61 @@ +[console_scripts] +easy_install = setuptools.command.easy_install:main + +[distutils.commands] +alias = setuptools.command.alias:alias +bdist_egg = setuptools.command.bdist_egg:bdist_egg +bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm +bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst +build_ext = setuptools.command.build_ext:build_ext +build_py = setuptools.command.build_py:build_py +develop = setuptools.command.develop:develop +easy_install = setuptools.command.easy_install:easy_install +egg_info = setuptools.command.egg_info:egg_info +install = setuptools.command.install:install +install_egg_info = setuptools.command.install_egg_info:install_egg_info +install_lib = setuptools.command.install_lib:install_lib +install_scripts = setuptools.command.install_scripts:install_scripts +register = setuptools.command.register:register +rotate = setuptools.command.rotate:rotate +saveopts = setuptools.command.saveopts:saveopts +sdist = setuptools.command.sdist:sdist +setopt = setuptools.command.setopt:setopt +test = setuptools.command.test:test +upload = setuptools.command.upload:upload +upload_docs = setuptools.command.upload_docs:upload_docs + +[distutils.setup_keywords] +convert_2to3_doctests = setuptools.dist:assert_string_list +dependency_links = setuptools.dist:assert_string_list +eager_resources = setuptools.dist:assert_string_list +entry_points = setuptools.dist:check_entry_points +exclude_package_data = setuptools.dist:check_package_data +extras_require = setuptools.dist:check_extras +include_package_data = setuptools.dist:assert_bool +install_requires = setuptools.dist:check_requirements +namespace_packages = setuptools.dist:check_nsp +package_data = setuptools.dist:check_package_data +packages = setuptools.dist:check_packages +setup_requires = setuptools.dist:check_requirements +test_loader = setuptools.dist:check_importable +test_runner = setuptools.dist:check_importable +test_suite = setuptools.dist:check_test_suite +tests_require = setuptools.dist:check_requirements +use_2to3 = setuptools.dist:assert_bool +use_2to3_exclude_fixers = setuptools.dist:assert_string_list +use_2to3_fixers = setuptools.dist:assert_string_list +zip_safe = setuptools.dist:assert_bool + +[egg_info.writers] +PKG-INFO = setuptools.command.egg_info:write_pkg_info +dependency_links.txt = setuptools.command.egg_info:overwrite_arg +depends.txt = setuptools.command.egg_info:warn_depends_obsolete +eager_resources.txt = setuptools.command.egg_info:overwrite_arg +entry_points.txt = setuptools.command.egg_info:write_entries +namespace_packages.txt = setuptools.command.egg_info:overwrite_arg +requires.txt = setuptools.command.egg_info:write_requirements +top_level.txt = setuptools.command.egg_info:write_toplevel_names + +[setuptools.installation] +eggsecutable = setuptools.command.easy_install:bootstrap + diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/metadata.json b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/metadata.json new file mode 100644 index 0000000..a5427bf --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Archiving :: Packaging", "Topic :: System :: Systems Administration", "Topic :: Utilities"], "extensions": {"python.commands": {"wrap_console": {"easy_install": "setuptools.command.easy_install:main"}}, "python.details": {"contacts": [{"email": "distutils-sig@python.org", "name": "Python Packaging Authority", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/pypa/setuptools"}}, "python.exports": {"console_scripts": {"easy_install": "setuptools.command.easy_install:main"}, "distutils.commands": {"alias": "setuptools.command.alias:alias", "bdist_egg": "setuptools.command.bdist_egg:bdist_egg", "bdist_rpm": "setuptools.command.bdist_rpm:bdist_rpm", "bdist_wininst": "setuptools.command.bdist_wininst:bdist_wininst", "build_ext": "setuptools.command.build_ext:build_ext", "build_py": "setuptools.command.build_py:build_py", "develop": "setuptools.command.develop:develop", "easy_install": "setuptools.command.easy_install:easy_install", "egg_info": "setuptools.command.egg_info:egg_info", "install": "setuptools.command.install:install", "install_egg_info": "setuptools.command.install_egg_info:install_egg_info", "install_lib": "setuptools.command.install_lib:install_lib", "install_scripts": "setuptools.command.install_scripts:install_scripts", "register": "setuptools.command.register:register", "rotate": "setuptools.command.rotate:rotate", "saveopts": "setuptools.command.saveopts:saveopts", "sdist": "setuptools.command.sdist:sdist", "setopt": "setuptools.command.setopt:setopt", "test": "setuptools.command.test:test", "upload": "setuptools.command.upload:upload", "upload_docs": "setuptools.command.upload_docs:upload_docs"}, "distutils.setup_keywords": {"convert_2to3_doctests": "setuptools.dist:assert_string_list", "dependency_links": "setuptools.dist:assert_string_list", "eager_resources": "setuptools.dist:assert_string_list", "entry_points": "setuptools.dist:check_entry_points", "exclude_package_data": "setuptools.dist:check_package_data", "extras_require": "setuptools.dist:check_extras", "include_package_data": "setuptools.dist:assert_bool", "install_requires": "setuptools.dist:check_requirements", "namespace_packages": "setuptools.dist:check_nsp", "package_data": "setuptools.dist:check_package_data", "packages": "setuptools.dist:check_packages", "setup_requires": "setuptools.dist:check_requirements", "test_loader": "setuptools.dist:check_importable", "test_runner": "setuptools.dist:check_importable", "test_suite": "setuptools.dist:check_test_suite", "tests_require": "setuptools.dist:check_requirements", "use_2to3": "setuptools.dist:assert_bool", "use_2to3_exclude_fixers": "setuptools.dist:assert_string_list", "use_2to3_fixers": "setuptools.dist:assert_string_list", "zip_safe": "setuptools.dist:assert_bool"}, "egg_info.writers": {"PKG-INFO": "setuptools.command.egg_info:write_pkg_info", "dependency_links.txt": "setuptools.command.egg_info:overwrite_arg", "depends.txt": "setuptools.command.egg_info:warn_depends_obsolete", "eager_resources.txt": "setuptools.command.egg_info:overwrite_arg", "entry_points.txt": "setuptools.command.egg_info:write_entries", "namespace_packages.txt": "setuptools.command.egg_info:overwrite_arg", "requires.txt": "setuptools.command.egg_info:write_requirements", "top_level.txt": "setuptools.command.egg_info:write_toplevel_names"}, "setuptools.installation": {"eggsecutable": "setuptools.command.easy_install:bootstrap"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["CPAN", "PyPI", "distutils", "eggs", "package", "management"], "metadata_version": "2.0", "name": "setuptools", "summary": "Easily download, build, install, upgrade, and uninstall Python packages", "version": "20.7.0"} \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/top_level.txt b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/top_level.txt new file mode 100644 index 0000000..4577c6a --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/top_level.txt @@ -0,0 +1,3 @@ +easy_install +pkg_resources +setuptools diff --git a/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/zip-safe b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools-20.7.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/lib/python3.5/site-packages/setuptools/__init__.py b/venv/lib/python3.5/site-packages/setuptools/__init__.py new file mode 100644 index 0000000..67b57e4 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/__init__.py @@ -0,0 +1,169 @@ +"""Extensions to the 'distutils' for large or complex distributions""" + +import os +import functools +import distutils.core +import distutils.filelist +from distutils.core import Command as _Command +from distutils.util import convert_path +from fnmatch import fnmatchcase + +from setuptools.extern.six.moves import filterfalse, map + +import setuptools.version +from setuptools.extension import Extension +from setuptools.dist import Distribution, Feature, _get_unpatched +from setuptools.depends import Require + +__all__ = [ + 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', + 'find_packages' +] + +__version__ = setuptools.version.__version__ + +bootstrap_install_from = None + +# If we run 2to3 on .py files, should we also convert docstrings? +# Default: yes; assume that we can detect doctests reliably +run_2to3_on_doctests = True +# Standard package names for fixer packages +lib2to3_fixer_packages = ['lib2to3.fixes'] + + +class PackageFinder(object): + @classmethod + def find(cls, where='.', exclude=(), include=('*',)): + """Return a list all Python packages found within directory 'where' + + 'where' should be supplied as a "cross-platform" (i.e. URL-style) + path; it will be converted to the appropriate local path syntax. + 'exclude' is a sequence of package names to exclude; '*' can be used + as a wildcard in the names, such that 'foo.*' will exclude all + subpackages of 'foo' (but not 'foo' itself). + + 'include' is a sequence of package names to include. If it's + specified, only the named packages will be included. If it's not + specified, all found packages will be included. 'include' can contain + shell style wildcard patterns just like 'exclude'. + + The list of included packages is built up first and then any + explicitly excluded packages are removed from it. + """ + out = cls._find_packages_iter(convert_path(where)) + out = cls.require_parents(out) + includes = cls._build_filter(*include) + excludes = cls._build_filter('ez_setup', '*__pycache__', *exclude) + out = filter(includes, out) + out = filterfalse(excludes, out) + return list(out) + + @staticmethod + def require_parents(packages): + """ + Exclude any apparent package that apparently doesn't include its + parent. + + For example, exclude 'foo.bar' if 'foo' is not present. + """ + found = [] + for pkg in packages: + base, sep, child = pkg.rpartition('.') + if base and base not in found: + continue + found.append(pkg) + yield pkg + + @staticmethod + def _candidate_dirs(base_path): + """ + Return all dirs in base_path that might be packages. + """ + has_dot = lambda name: '.' in name + for root, dirs, files in os.walk(base_path, followlinks=True): + # Exclude directories that contain a period, as they cannot be + # packages. Mutate the list to avoid traversal. + dirs[:] = filterfalse(has_dot, dirs) + for dir in dirs: + yield os.path.relpath(os.path.join(root, dir), base_path) + + @classmethod + def _find_packages_iter(cls, base_path): + candidates = cls._candidate_dirs(base_path) + return ( + path.replace(os.path.sep, '.') + for path in candidates + if cls._looks_like_package(os.path.join(base_path, path)) + ) + + @staticmethod + def _looks_like_package(path): + return os.path.isfile(os.path.join(path, '__init__.py')) + + @staticmethod + def _build_filter(*patterns): + """ + Given a list of patterns, return a callable that will be true only if + the input matches one of the patterns. + """ + return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) + +class PEP420PackageFinder(PackageFinder): + @staticmethod + def _looks_like_package(path): + return True + +find_packages = PackageFinder.find + +setup = distutils.core.setup + +_Command = _get_unpatched(_Command) + +class Command(_Command): + __doc__ = _Command.__doc__ + + command_consumes_arguments = False + + def __init__(self, dist, **kw): + """ + Construct the command for dist, updating + vars(self) with any keyword parameters. + """ + _Command.__init__(self, dist) + vars(self).update(kw) + + def reinitialize_command(self, command, reinit_subcommands=0, **kw): + cmd = _Command.reinitialize_command(self, command, reinit_subcommands) + vars(cmd).update(kw) + return cmd + +# we can't patch distutils.cmd, alas +distutils.core.Command = Command + + +def _find_all_simple(path): + """ + Find all files under 'path' + """ + results = ( + os.path.join(base, file) + for base, dirs, files in os.walk(path, followlinks=True) + for file in files + ) + return filter(os.path.isfile, results) + + +def findall(dir=os.curdir): + """ + Find all files under 'dir' and return the list of full filenames. + Unless dir is '.', return full filenames with dir prepended. + """ + files = _find_all_simple(dir) + if dir == os.curdir: + make_rel = functools.partial(os.path.relpath, start=dir) + files = map(make_rel, files) + return list(files) + + +# fix findall bug in distutils (http://bugs.python.org/issue12885) +distutils.filelist.findall = findall diff --git a/venv/lib/python3.5/site-packages/setuptools/archive_util.py b/venv/lib/python3.5/site-packages/setuptools/archive_util.py new file mode 100644 index 0000000..b3c9fa5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/archive_util.py @@ -0,0 +1,170 @@ +"""Utilities for extracting common archive formats""" + + +__all__ = [ + "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", + "UnrecognizedFormat", "extraction_drivers", "unpack_directory", +] + +import zipfile +import tarfile +import os +import shutil +import posixpath +import contextlib +from pkg_resources import ensure_directory, ContextualZipFile +from distutils.errors import DistutilsError + +class UnrecognizedFormat(DistutilsError): + """Couldn't recognize the archive type""" + +def default_filter(src,dst): + """The default progress/filter callback; returns True for all files""" + return dst + + +def unpack_archive(filename, extract_dir, progress_filter=default_filter, + drivers=None): + """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat`` + + `progress_filter` is a function taking two arguments: a source path + internal to the archive ('/'-separated), and a filesystem path where it + will be extracted. The callback must return the desired extract path + (which may be the same as the one passed in), or else ``None`` to skip + that file or directory. The callback can thus be used to report on the + progress of the extraction, as well as to filter the items extracted or + alter their extraction paths. + + `drivers`, if supplied, must be a non-empty sequence of functions with the + same signature as this function (minus the `drivers` argument), that raise + ``UnrecognizedFormat`` if they do not support extracting the designated + archive type. The `drivers` are tried in sequence until one is found that + does not raise an error, or until all are exhausted (in which case + ``UnrecognizedFormat`` is raised). If you do not supply a sequence of + drivers, the module's ``extraction_drivers`` constant will be used, which + means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that + order. + """ + for driver in drivers or extraction_drivers: + try: + driver(filename, extract_dir, progress_filter) + except UnrecognizedFormat: + continue + else: + return + else: + raise UnrecognizedFormat( + "Not a recognized archive type: %s" % filename + ) + + +def unpack_directory(filename, extract_dir, progress_filter=default_filter): + """"Unpack" a directory, using the same interface as for archives + + Raises ``UnrecognizedFormat`` if `filename` is not a directory + """ + if not os.path.isdir(filename): + raise UnrecognizedFormat("%s is not a directory" % filename) + + paths = { + filename: ('', extract_dir), + } + for base, dirs, files in os.walk(filename): + src, dst = paths[base] + for d in dirs: + paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d) + for f in files: + target = os.path.join(dst, f) + target = progress_filter(src + f, target) + if not target: + # skip non-files + continue + ensure_directory(target) + f = os.path.join(base, f) + shutil.copyfile(f, target) + shutil.copystat(f, target) + + +def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): + """Unpack zip `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined + by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + + if not zipfile.is_zipfile(filename): + raise UnrecognizedFormat("%s is not a zip file" % (filename,)) + + with ContextualZipFile(filename) as z: + for info in z.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name.split('/'): + continue + + target = os.path.join(extract_dir, *name.split('/')) + target = progress_filter(name, target) + if not target: + continue + if name.endswith('/'): + # directory + ensure_directory(target) + else: + # file + ensure_directory(target) + data = z.read(info.filename) + with open(target, 'wb') as f: + f.write(data) + unix_attributes = info.external_attr >> 16 + if unix_attributes: + os.chmod(target, unix_attributes) + + +def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined + by ``tarfile.open()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise UnrecognizedFormat( + "%s is not a compressed or uncompressed tar file" % (filename,) + ) + with contextlib.closing(tarobj): + # don't do any chowning! + tarobj.chown = lambda *args: None + for member in tarobj: + name = member.name + # don't extract absolute paths or ones with .. in them + if not name.startswith('/') and '..' not in name.split('/'): + prelim_dst = os.path.join(extract_dir, *name.split('/')) + + # resolve any links and to extract the link targets as normal + # files + while member is not None and (member.islnk() or member.issym()): + linkpath = member.linkname + if member.issym(): + base = posixpath.dirname(member.name) + linkpath = posixpath.join(base, linkpath) + linkpath = posixpath.normpath(linkpath) + member = tarobj._getmember(linkpath) + + if member is not None and (member.isfile() or member.isdir()): + final_dst = progress_filter(name, prelim_dst) + if final_dst: + if final_dst.endswith(os.sep): + final_dst = final_dst[:-1] + try: + # XXX Ugh + tarobj._extract_member(member, final_dst) + except tarfile.ExtractError: + # chown/chmod/mkfifo/mknode/makedev failed + pass + return True + +extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/venv/lib/python3.5/site-packages/setuptools/command/__init__.py b/venv/lib/python3.5/site-packages/setuptools/command/__init__.py new file mode 100644 index 0000000..3fb2f6d --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/__init__.py @@ -0,0 +1,18 @@ +__all__ = [ + 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', + 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', + 'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts', + 'register', 'bdist_wininst', 'upload_docs', 'upload', +] + +from distutils.command.bdist import bdist +import sys + +from setuptools.command import install_scripts + + +if 'egg' not in bdist.format_commands: + bdist.format_command['egg'] = ('bdist_egg', "Python .egg file") + bdist.format_commands.append('egg') + +del bdist, sys diff --git a/venv/lib/python3.5/site-packages/setuptools/command/alias.py b/venv/lib/python3.5/site-packages/setuptools/command/alias.py new file mode 100644 index 0000000..4532b1c --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/alias.py @@ -0,0 +1,80 @@ +from distutils.errors import DistutilsOptionError + +from setuptools.extern.six.moves import map + +from setuptools.command.setopt import edit_config, option_base, config_file + + +def shquote(arg): + """Quote an argument for later parsing by shlex.split()""" + for c in '"', "'", "\\", "#": + if c in arg: + return repr(arg) + if arg.split() != [arg]: + return repr(arg) + return arg + + +class alias(option_base): + """Define a shortcut that invokes one or more commands""" + + description = "define a shortcut to invoke one or more commands" + command_consumes_arguments = True + + user_options = [ + ('remove', 'r', 'remove (unset) the alias'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.args = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.remove and len(self.args) != 1: + raise DistutilsOptionError( + "Must specify exactly one argument (the alias name) when " + "using --remove" + ) + + def run(self): + aliases = self.distribution.get_option_dict('aliases') + + if not self.args: + print("Command Aliases") + print("---------------") + for alias in aliases: + print("setup.py alias", format_alias(alias, aliases)) + return + + elif len(self.args) == 1: + alias, = self.args + if self.remove: + command = None + elif alias in aliases: + print("setup.py alias", format_alias(alias, aliases)) + return + else: + print("No alias definition found for %r" % alias) + return + else: + alias = self.args[0] + command = ' '.join(map(shquote, self.args[1:])) + + edit_config(self.filename, {'aliases': {alias: command}}, self.dry_run) + + +def format_alias(name, aliases): + source, command = aliases[name] + if source == config_file('global'): + source = '--global-config ' + elif source == config_file('user'): + source = '--user-config ' + elif source == config_file('local'): + source = '' + else: + source = '--filename=%r' % source + return source + name + ' ' + command diff --git a/venv/lib/python3.5/site-packages/setuptools/command/bdist_egg.py b/venv/lib/python3.5/site-packages/setuptools/command/bdist_egg.py new file mode 100644 index 0000000..9cebd7f --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/bdist_egg.py @@ -0,0 +1,471 @@ +"""setuptools.command.bdist_egg + +Build .egg distributions""" + +from distutils.errors import DistutilsSetupError +from distutils.dir_util import remove_tree, mkpath +from distutils import log +from types import CodeType +import sys +import os +import marshal +import textwrap + +from setuptools.extern import six + +from pkg_resources import get_build_platform, Distribution, ensure_directory +from pkg_resources import EntryPoint +from setuptools.extension import Library +from setuptools import Command + +try: + # Python 2.7 or >=3.2 + from sysconfig import get_path, get_python_version + + def _get_purelib(): + return get_path("purelib") +except ImportError: + from distutils.sysconfig import get_python_lib, get_python_version + + def _get_purelib(): + return get_python_lib(False) + + +def strip_module(filename): + if '.' in filename: + filename = os.path.splitext(filename)[0] + if filename.endswith('module'): + filename = filename[:-6] + return filename + + +def write_stub(resource, pyfile): + _stub_template = textwrap.dedent(""" + def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__, %r) + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) + __bootstrap__() + """).lstrip() + with open(pyfile, 'w') as f: + f.write(_stub_template % resource) + + +class bdist_egg(Command): + description = "create an \"egg\" distribution" + + user_options = [ + ('bdist-dir=', 'b', + "temporary directory for creating the distribution"), + ('plat-name=', 'p', "platform name to embed in generated filenames " + "(default: %s)" % get_build_platform()), + ('exclude-source-files', None, + "remove all .py files from the generated egg"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ] + + boolean_options = [ + 'keep-temp', 'skip-build', 'exclude-source-files' + ] + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = 0 + self.egg_output = None + self.exclude_source_files = None + + def finalize_options(self): + ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info") + self.egg_info = ei_cmd.egg_info + + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'egg') + + if self.plat_name is None: + self.plat_name = get_build_platform() + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + + if self.egg_output is None: + + # Compute filename of the output egg + basename = Distribution( + None, None, ei_cmd.egg_name, ei_cmd.egg_version, + get_python_version(), + self.distribution.has_ext_modules() and self.plat_name + ).egg_name() + + self.egg_output = os.path.join(self.dist_dir, basename + '.egg') + + def do_install_data(self): + # Hack for packages that install data to install's --install-lib + self.get_finalized_command('install').install_lib = self.bdist_dir + + site_packages = os.path.normcase(os.path.realpath(_get_purelib())) + old, self.distribution.data_files = self.distribution.data_files, [] + + for item in old: + if isinstance(item, tuple) and len(item) == 2: + if os.path.isabs(item[0]): + realpath = os.path.realpath(item[0]) + normalized = os.path.normcase(realpath) + if normalized == site_packages or normalized.startswith( + site_packages + os.sep + ): + item = realpath[len(site_packages) + 1:], item[1] + # XXX else: raise ??? + self.distribution.data_files.append(item) + + try: + log.info("installing package data to %s" % self.bdist_dir) + self.call_command('install_data', force=0, root=None) + finally: + self.distribution.data_files = old + + def get_outputs(self): + return [self.egg_output] + + def call_command(self, cmdname, **kw): + """Invoke reinitialized command `cmdname` with keyword args""" + for dirname in INSTALL_DIRECTORY_ATTRS: + kw.setdefault(dirname, self.bdist_dir) + kw.setdefault('skip_build', self.skip_build) + kw.setdefault('dry_run', self.dry_run) + cmd = self.reinitialize_command(cmdname, **kw) + self.run_command(cmdname) + return cmd + + def run(self): + # Generate metadata first + self.run_command("egg_info") + # We run install_lib before install_data, because some data hacks + # pull their data path from the install_lib command. + log.info("installing library code to %s" % self.bdist_dir) + instcmd = self.get_finalized_command('install') + old_root = instcmd.root + instcmd.root = None + if self.distribution.has_c_libraries() and not self.skip_build: + self.run_command('build_clib') + cmd = self.call_command('install_lib', warn_dir=0) + instcmd.root = old_root + + all_outputs, ext_outputs = self.get_ext_outputs() + self.stubs = [] + to_compile = [] + for (p, ext_name) in enumerate(ext_outputs): + filename, ext = os.path.splitext(ext_name) + pyfile = os.path.join(self.bdist_dir, strip_module(filename) + + '.py') + self.stubs.append(pyfile) + log.info("creating stub loader for %s" % ext_name) + if not self.dry_run: + write_stub(os.path.basename(ext_name), pyfile) + to_compile.append(pyfile) + ext_outputs[p] = ext_name.replace(os.sep, '/') + + if to_compile: + cmd.byte_compile(to_compile) + if self.distribution.data_files: + self.do_install_data() + + # Make the EGG-INFO directory + archive_root = self.bdist_dir + egg_info = os.path.join(archive_root, 'EGG-INFO') + self.mkpath(egg_info) + if self.distribution.scripts: + script_dir = os.path.join(egg_info, 'scripts') + log.info("installing scripts to %s" % script_dir) + self.call_command('install_scripts', install_dir=script_dir, + no_ep=1) + + self.copy_metadata_to(egg_info) + native_libs = os.path.join(egg_info, "native_libs.txt") + if all_outputs: + log.info("writing %s" % native_libs) + if not self.dry_run: + ensure_directory(native_libs) + libs_file = open(native_libs, 'wt') + libs_file.write('\n'.join(all_outputs)) + libs_file.write('\n') + libs_file.close() + elif os.path.isfile(native_libs): + log.info("removing %s" % native_libs) + if not self.dry_run: + os.unlink(native_libs) + + write_safety_flag( + os.path.join(archive_root, 'EGG-INFO'), self.zip_safe() + ) + + if os.path.exists(os.path.join(self.egg_info, 'depends.txt')): + log.warn( + "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." + ) + + if self.exclude_source_files: + self.zap_pyfiles() + + # Make the archive + make_zipfile(self.egg_output, archive_root, verbose=self.verbose, + dry_run=self.dry_run, mode=self.gen_header()) + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + # Add to 'Distribution.dist_files' so that the "upload" command works + getattr(self.distribution, 'dist_files', []).append( + ('bdist_egg', get_python_version(), self.egg_output)) + + def zap_pyfiles(self): + log.info("Removing .py files from temporary directory") + for base, dirs, files in walk_egg(self.bdist_dir): + for name in files: + if name.endswith('.py'): + path = os.path.join(base, name) + log.debug("Deleting %s", path) + os.unlink(path) + + def zip_safe(self): + safe = getattr(self.distribution, 'zip_safe', None) + if safe is not None: + return safe + log.warn("zip_safe flag not set; analyzing archive contents...") + return analyze_egg(self.bdist_dir, self.stubs) + + def gen_header(self): + epm = EntryPoint.parse_map(self.distribution.entry_points or '') + ep = epm.get('setuptools.installation', {}).get('eggsecutable') + if ep is None: + return 'w' # not an eggsecutable, do it the usual way. + + if not ep.attrs or ep.extras: + raise DistutilsSetupError( + "eggsecutable entry point (%r) cannot have 'extras' " + "or refer to a module" % (ep,) + ) + + pyver = sys.version[:3] + pkg = ep.module_name + full = '.'.join(ep.attrs) + base = ep.attrs[0] + basename = os.path.basename(self.egg_output) + + header = ( + "#!/bin/sh\n" + 'if [ `basename $0` = "%(basename)s" ]\n' + 'then exec python%(pyver)s -c "' + "import sys, os; sys.path.insert(0, os.path.abspath('$0')); " + "from %(pkg)s import %(base)s; sys.exit(%(full)s())" + '" "$@"\n' + 'else\n' + ' echo $0 is not the correct name for this egg file.\n' + ' echo Please rename it back to %(basename)s and try again.\n' + ' exec false\n' + 'fi\n' + ) % locals() + + if not self.dry_run: + mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run) + f = open(self.egg_output, 'w') + f.write(header) + f.close() + return 'a' + + def copy_metadata_to(self, target_dir): + "Copy metadata (egg info) to the target_dir" + # normalize the path (so that a forward-slash in egg_info will + # match using startswith below) + norm_egg_info = os.path.normpath(self.egg_info) + prefix = os.path.join(norm_egg_info, '') + for path in self.ei_cmd.filelist.files: + if path.startswith(prefix): + target = os.path.join(target_dir, path[len(prefix):]) + ensure_directory(target) + self.copy_file(path, target) + + def get_ext_outputs(self): + """Get a list of relative paths to C extensions in the output distro""" + + all_outputs = [] + ext_outputs = [] + + paths = {self.bdist_dir: ''} + for base, dirs, files in os.walk(self.bdist_dir): + for filename in files: + if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: + all_outputs.append(paths[base] + filename) + for filename in dirs: + paths[os.path.join(base, filename)] = (paths[base] + + filename + '/') + + if self.distribution.has_ext_modules(): + build_cmd = self.get_finalized_command('build_ext') + for ext in build_cmd.extensions: + if isinstance(ext, Library): + continue + fullname = build_cmd.get_ext_fullname(ext.name) + filename = build_cmd.get_ext_filename(fullname) + if not os.path.basename(filename).startswith('dl-'): + if os.path.exists(os.path.join(self.bdist_dir, filename)): + ext_outputs.append(filename) + + return all_outputs, ext_outputs + + +NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) + + +def walk_egg(egg_dir): + """Walk an unpacked egg's contents, skipping the metadata directory""" + walker = os.walk(egg_dir) + base, dirs, files = next(walker) + if 'EGG-INFO' in dirs: + dirs.remove('EGG-INFO') + yield base, dirs, files + for bdf in walker: + yield bdf + + +def analyze_egg(egg_dir, stubs): + # check for existing flag in EGG-INFO + for flag, fn in safety_flags.items(): + if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)): + return flag + if not can_scan(): + return False + safe = True + for base, dirs, files in walk_egg(egg_dir): + for name in files: + if name.endswith('.py') or name.endswith('.pyw'): + continue + elif name.endswith('.pyc') or name.endswith('.pyo'): + # always scan, even if we already know we're not safe + safe = scan_module(egg_dir, base, name, stubs) and safe + return safe + + +def write_safety_flag(egg_dir, safe): + # Write or remove zip safety flag file(s) + for flag, fn in safety_flags.items(): + fn = os.path.join(egg_dir, fn) + if os.path.exists(fn): + if safe is None or bool(safe) != flag: + os.unlink(fn) + elif safe is not None and bool(safe) == flag: + f = open(fn, 'wt') + f.write('\n') + f.close() + + +safety_flags = { + True: 'zip-safe', + False: 'not-zip-safe', +} + + +def scan_module(egg_dir, base, name, stubs): + """Check whether module possibly uses unsafe-for-zipfile stuff""" + + filename = os.path.join(base, name) + if filename[:-1] in stubs: + return True # Extension module + pkg = base[len(egg_dir) + 1:].replace(os.sep, '.') + module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0] + if sys.version_info < (3, 3): + skip = 8 # skip magic & date + else: + skip = 12 # skip magic & date & file size + f = open(filename, 'rb') + f.read(skip) + code = marshal.load(f) + f.close() + safe = True + symbols = dict.fromkeys(iter_symbols(code)) + for bad in ['__file__', '__path__']: + if bad in symbols: + log.warn("%s: module references %s", module, bad) + safe = False + if 'inspect' in symbols: + for bad in [ + 'getsource', 'getabsfile', 'getsourcefile', 'getfile' + 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', + 'getinnerframes', 'getouterframes', 'stack', 'trace' + ]: + if bad in symbols: + log.warn("%s: module MAY be using inspect.%s", module, bad) + safe = False + return safe + + +def iter_symbols(code): + """Yield names and strings used by `code` and its nested code objects""" + for name in code.co_names: + yield name + for const in code.co_consts: + if isinstance(const, six.string_types): + yield const + elif isinstance(const, CodeType): + for name in iter_symbols(const): + yield name + + +def can_scan(): + if not sys.platform.startswith('java') and sys.platform != 'cli': + # CPython, PyPy, etc. + return True + log.warn("Unable to analyze compiled code on this platform.") + log.warn("Please ask the author to include a 'zip_safe'" + " setting (either True or False) in the package's setup.py") + +# Attribute names of options for commands that might need to be convinced to +# install to the egg build directory + +INSTALL_DIRECTORY_ATTRS = [ + 'install_lib', 'install_dir', 'install_data', 'install_base' +] + + +def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True, + mode='w'): + """Create a zip file from all the files under 'base_dir'. The output + zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" + Python module (if available) or the InfoZIP "zip" utility (if installed + and found on the default search path). If neither tool is available, + raises DistutilsExecError. Returns the name of the output zip file. + """ + import zipfile + + mkpath(os.path.dirname(zip_filename), dry_run=dry_run) + log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) + + def visit(z, dirname, names): + for name in names: + path = os.path.normpath(os.path.join(dirname, name)) + if os.path.isfile(path): + p = path[len(base_dir) + 1:] + if not dry_run: + z.write(path, p) + log.debug("adding '%s'" % p) + + compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED + if not dry_run: + z = zipfile.ZipFile(zip_filename, mode, compression=compression) + for dirname, dirs, files in os.walk(base_dir): + visit(z, dirname, files) + z.close() + else: + for dirname, dirs, files in os.walk(base_dir): + visit(None, dirname, files) + return zip_filename diff --git a/venv/lib/python3.5/site-packages/setuptools/command/bdist_rpm.py b/venv/lib/python3.5/site-packages/setuptools/command/bdist_rpm.py new file mode 100644 index 0000000..7073092 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/bdist_rpm.py @@ -0,0 +1,43 @@ +import distutils.command.bdist_rpm as orig + + +class bdist_rpm(orig.bdist_rpm): + """ + Override the default bdist_rpm behavior to do the following: + + 1. Run egg_info to ensure the name and version are properly calculated. + 2. Always run 'install' using --single-version-externally-managed to + disable eggs in RPM distributions. + 3. Replace dash with underscore in the version numbers for better RPM + compatibility. + """ + + def run(self): + # ensure distro name is up-to-date + self.run_command('egg_info') + + orig.bdist_rpm.run(self) + + def _make_spec_file(self): + version = self.distribution.get_version() + rpmversion = version.replace('-', '_') + spec = orig.bdist_rpm._make_spec_file(self) + line23 = '%define version ' + version + line24 = '%define version ' + rpmversion + spec = [ + line.replace( + "Source0: %{name}-%{version}.tar", + "Source0: %{name}-%{unmangled_version}.tar" + ).replace( + "setup.py install ", + "setup.py install --single-version-externally-managed " + ).replace( + "%setup", + "%setup -n %{name}-%{unmangled_version}" + ).replace(line23, line24) + for line in spec + ] + insert_loc = spec.index(line24) + 1 + unmangled_version = "%define unmangled_version " + version + spec.insert(insert_loc, unmangled_version) + return spec diff --git a/venv/lib/python3.5/site-packages/setuptools/command/bdist_wininst.py b/venv/lib/python3.5/site-packages/setuptools/command/bdist_wininst.py new file mode 100644 index 0000000..073de97 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/bdist_wininst.py @@ -0,0 +1,21 @@ +import distutils.command.bdist_wininst as orig + + +class bdist_wininst(orig.bdist_wininst): + def reinitialize_command(self, command, reinit_subcommands=0): + """ + Supplement reinitialize_command to work around + http://bugs.python.org/issue20819 + """ + cmd = self.distribution.reinitialize_command( + command, reinit_subcommands) + if command in ('install', 'install_lib'): + cmd.install_lib = None + return cmd + + def run(self): + self._is_running = True + try: + orig.bdist_wininst.run(self) + finally: + self._is_running = False diff --git a/venv/lib/python3.5/site-packages/setuptools/command/build_ext.py b/venv/lib/python3.5/site-packages/setuptools/command/build_ext.py new file mode 100644 index 0000000..92e4a18 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/build_ext.py @@ -0,0 +1,296 @@ +from distutils.command.build_ext import build_ext as _du_build_ext +from distutils.file_util import copy_file +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler +from distutils.errors import DistutilsError +from distutils import log +import os +import sys +import itertools + +from setuptools.extension import Library + +try: + # Attempt to use Cython for building extensions, if available + from Cython.Distutils.build_ext import build_ext as _build_ext +except ImportError: + _build_ext = _du_build_ext + +try: + # Python 2.7 or >=3.2 + from sysconfig import _CONFIG_VARS +except ImportError: + from distutils.sysconfig import get_config_var + + get_config_var("LDSHARED") # make sure _config_vars is initialized + del get_config_var + from distutils.sysconfig import _config_vars as _CONFIG_VARS + +have_rtld = False +use_stubs = False +libtype = 'shared' + +if sys.platform == "darwin": + use_stubs = True +elif os.name != 'nt': + try: + import dl + use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW') + except ImportError: + pass + + +if_dl = lambda s: s if have_rtld else '' + +class build_ext(_build_ext): + def run(self): + """Build extensions in build directory, then copy if --inplace""" + old_inplace, self.inplace = self.inplace, 0 + _build_ext.run(self) + self.inplace = old_inplace + if old_inplace: + self.copy_extensions_to_source() + + def copy_extensions_to_source(self): + build_py = self.get_finalized_command('build_py') + for ext in self.extensions: + fullname = self.get_ext_fullname(ext.name) + filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[:-1]) + package_dir = build_py.get_package_dir(package) + dest_filename = os.path.join(package_dir, + os.path.basename(filename)) + src_filename = os.path.join(self.build_lib, filename) + + # Always copy, even if source is older than destination, to ensure + # that the right extensions for the current Python/platform are + # used. + copy_file( + src_filename, dest_filename, verbose=self.verbose, + dry_run=self.dry_run + ) + if ext._needs_stub: + self.write_stub(package_dir or os.curdir, ext, True) + + def get_ext_filename(self, fullname): + filename = _build_ext.get_ext_filename(self, fullname) + if fullname in self.ext_map: + ext = self.ext_map[fullname] + if isinstance(ext, Library): + fn, ext = os.path.splitext(filename) + return self.shlib_compiler.library_filename(fn, libtype) + elif use_stubs and ext._links_to_dynamic: + d, fn = os.path.split(filename) + return os.path.join(d, 'dl-' + fn) + return filename + + def initialize_options(self): + _build_ext.initialize_options(self) + self.shlib_compiler = None + self.shlibs = [] + self.ext_map = {} + + def finalize_options(self): + _build_ext.finalize_options(self) + self.extensions = self.extensions or [] + self.check_extensions_list(self.extensions) + self.shlibs = [ext for ext in self.extensions + if isinstance(ext, Library)] + if self.shlibs: + self.setup_shlib_compiler() + for ext in self.extensions: + ext._full_name = self.get_ext_fullname(ext.name) + for ext in self.extensions: + fullname = ext._full_name + self.ext_map[fullname] = ext + + # distutils 3.1 will also ask for module names + # XXX what to do with conflicts? + self.ext_map[fullname.split('.')[-1]] = ext + + ltd = self.shlibs and self.links_to_dynamic(ext) or False + ns = ltd and use_stubs and not isinstance(ext, Library) + ext._links_to_dynamic = ltd + ext._needs_stub = ns + filename = ext._file_name = self.get_ext_filename(fullname) + libdir = os.path.dirname(os.path.join(self.build_lib, filename)) + if ltd and libdir not in ext.library_dirs: + ext.library_dirs.append(libdir) + if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: + ext.runtime_library_dirs.append(os.curdir) + + def setup_shlib_compiler(self): + compiler = self.shlib_compiler = new_compiler( + compiler=self.compiler, dry_run=self.dry_run, force=self.force + ) + if sys.platform == "darwin": + tmp = _CONFIG_VARS.copy() + try: + # XXX Help! I don't have any idea whether these are right... + _CONFIG_VARS['LDSHARED'] = ( + "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup") + _CONFIG_VARS['CCSHARED'] = " -dynamiclib" + _CONFIG_VARS['SO'] = ".dylib" + customize_compiler(compiler) + finally: + _CONFIG_VARS.clear() + _CONFIG_VARS.update(tmp) + else: + customize_compiler(compiler) + + if self.include_dirs is not None: + compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name, value) in self.define: + compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + compiler.undefine_macro(macro) + if self.libraries is not None: + compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + compiler.set_link_objects(self.link_objects) + + # hack so distutils' build_extension() builds a library instead + compiler.link_shared_object = link_shared_object.__get__(compiler) + + def get_export_symbols(self, ext): + if isinstance(ext, Library): + return ext.export_symbols + return _build_ext.get_export_symbols(self, ext) + + def build_extension(self, ext): + ext._convert_pyx_sources_to_lang() + _compiler = self.compiler + try: + if isinstance(ext, Library): + self.compiler = self.shlib_compiler + _build_ext.build_extension(self, ext) + if ext._needs_stub: + cmd = self.get_finalized_command('build_py').build_lib + self.write_stub(cmd, ext) + finally: + self.compiler = _compiler + + def links_to_dynamic(self, ext): + """Return true if 'ext' links to a dynamic lib in the same package""" + # XXX this should check to ensure the lib is actually being built + # XXX as dynamic, and not just using a locally-found version or a + # XXX static-compiled version + libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) + pkg = '.'.join(ext._full_name.split('.')[:-1] + ['']) + return any(pkg + libname in libnames for libname in ext.libraries) + + def get_outputs(self): + return _build_ext.get_outputs(self) + self.__get_stubs_outputs() + + def __get_stubs_outputs(self): + # assemble the base name for each extension that needs a stub + ns_ext_bases = ( + os.path.join(self.build_lib, *ext._full_name.split('.')) + for ext in self.extensions + if ext._needs_stub + ) + # pair each base with the extension + pairs = itertools.product(ns_ext_bases, self.__get_output_extensions()) + return list(base + fnext for base, fnext in pairs) + + def __get_output_extensions(self): + yield '.py' + yield '.pyc' + if self.get_finalized_command('build_py').optimize: + yield '.pyo' + + def write_stub(self, output_dir, ext, compile=False): + log.info("writing stub loader for %s to %s", ext._full_name, + output_dir) + stub_file = (os.path.join(output_dir, *ext._full_name.split('.')) + + '.py') + if compile and os.path.exists(stub_file): + raise DistutilsError(stub_file + " already exists! Please delete.") + if not self.dry_run: + f = open(stub_file, 'w') + f.write( + '\n'.join([ + "def __bootstrap__():", + " global __bootstrap__, __file__, __loader__", + " import sys, os, pkg_resources, imp" + if_dl(", dl"), + " __file__ = pkg_resources.resource_filename" + "(__name__,%r)" + % os.path.basename(ext._file_name), + " del __bootstrap__", + " if '__loader__' in globals():", + " del __loader__", + if_dl(" old_flags = sys.getdlopenflags()"), + " old_dir = os.getcwd()", + " try:", + " os.chdir(os.path.dirname(__file__))", + if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), + " imp.load_dynamic(__name__,__file__)", + " finally:", + if_dl(" sys.setdlopenflags(old_flags)"), + " os.chdir(old_dir)", + "__bootstrap__()", + "" # terminal \n + ]) + ) + f.close() + if compile: + from distutils.util import byte_compile + + byte_compile([stub_file], optimize=0, + force=True, dry_run=self.dry_run) + optimize = self.get_finalized_command('install_lib').optimize + if optimize > 0: + byte_compile([stub_file], optimize=optimize, + force=True, dry_run=self.dry_run) + if os.path.exists(stub_file) and not self.dry_run: + os.unlink(stub_file) + + +if use_stubs or os.name == 'nt': + # Build shared libraries + # + def link_shared_object( + self, objects, output_libname, output_dir=None, libraries=None, + library_dirs=None, runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, + target_lang=None): + self.link( + self.SHARED_LIBRARY, objects, output_libname, + output_dir, libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs, + build_temp, target_lang + ) +else: + # Build static libraries everywhere else + libtype = 'static' + + def link_shared_object( + self, objects, output_libname, output_dir=None, libraries=None, + library_dirs=None, runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, + target_lang=None): + # XXX we need to either disallow these attrs on Library instances, + # or warn/abort here if set, or something... + # libraries=None, library_dirs=None, runtime_library_dirs=None, + # export_symbols=None, extra_preargs=None, extra_postargs=None, + # build_temp=None + + assert output_dir is None # distutils build_ext doesn't pass this + output_dir, filename = os.path.split(output_libname) + basename, ext = os.path.splitext(filename) + if self.library_filename("x").startswith('lib'): + # strip 'lib' prefix; this is kludgy if some platform uses + # a different prefix + basename = basename[3:] + + self.create_static_lib( + objects, basename, output_dir, debug, target_lang + ) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/build_py.py b/venv/lib/python3.5/site-packages/setuptools/command/build_py.py new file mode 100644 index 0000000..8623c77 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/build_py.py @@ -0,0 +1,222 @@ +from glob import glob +from distutils.util import convert_path +import distutils.command.build_py as orig +import os +import fnmatch +import textwrap +import io +import distutils.errors +import collections +import itertools + +from setuptools.extern.six.moves import map + +try: + from setuptools.lib2to3_ex import Mixin2to3 +except ImportError: + class Mixin2to3: + def run_2to3(self, files, doctests=True): + "do nothing" + + +class build_py(orig.build_py, Mixin2to3): + """Enhanced 'build_py' command that includes data files with packages + + The data files are specified via a 'package_data' argument to 'setup()'. + See 'setuptools.dist.Distribution' for more details. + + Also, this version of the 'build_py' command allows you to specify both + 'py_modules' and 'packages' in the same setup operation. + """ + + def finalize_options(self): + orig.build_py.finalize_options(self) + self.package_data = self.distribution.package_data + self.exclude_package_data = (self.distribution.exclude_package_data or + {}) + if 'data_files' in self.__dict__: + del self.__dict__['data_files'] + self.__updated_files = [] + self.__doctests_2to3 = [] + + def run(self): + """Build modules, packages, and copy data files to build directory""" + if not self.py_modules and not self.packages: + return + + if self.py_modules: + self.build_modules() + + if self.packages: + self.build_packages() + self.build_package_data() + + self.run_2to3(self.__updated_files, False) + self.run_2to3(self.__updated_files, True) + self.run_2to3(self.__doctests_2to3, True) + + # Only compile actual .py files, using our base class' idea of what our + # output files are. + self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0)) + + def __getattr__(self, attr): + "lazily compute data files" + if attr == 'data_files': + self.data_files = self._get_data_files() + return self.data_files + return orig.build_py.__getattr__(self, attr) + + def build_module(self, module, module_file, package): + outfile, copied = orig.build_py.build_module(self, module, module_file, + package) + if copied: + self.__updated_files.append(outfile) + return outfile, copied + + def _get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + self.analyze_manifest() + return list(map(self._get_pkg_data_files, self.packages or ())) + + def _get_pkg_data_files(self, package): + # Locate package source directory + src_dir = self.get_package_dir(package) + + # Compute package build directory + build_dir = os.path.join(*([self.build_lib] + package.split('.'))) + + # Strip directory from globbed filenames + filenames = [ + os.path.relpath(file, src_dir) + for file in self.find_data_files(package, src_dir) + ] + return package, src_dir, build_dir, filenames + + def find_data_files(self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = (self.package_data.get('', []) + + self.package_data.get(package, [])) + files = self.manifest_files.get(package, [])[:] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + files.extend(glob(os.path.join(src_dir, convert_path(pattern)))) + return self.exclude_data_files(package, src_dir, files) + + def build_package_data(self): + """Copy data files into build directory""" + for package, src_dir, build_dir, filenames in self.data_files: + for filename in filenames: + target = os.path.join(build_dir, filename) + self.mkpath(os.path.dirname(target)) + srcfile = os.path.join(src_dir, filename) + outf, copied = self.copy_file(srcfile, target) + srcfile = os.path.abspath(srcfile) + if (copied and + srcfile in self.distribution.convert_2to3_doctests): + self.__doctests_2to3.append(outf) + + def analyze_manifest(self): + self.manifest_files = mf = {} + if not self.distribution.include_package_data: + return + src_dirs = {} + for package in self.packages or (): + # Locate package source directory + src_dirs[assert_relative(self.get_package_dir(package))] = package + + self.run_command('egg_info') + ei_cmd = self.get_finalized_command('egg_info') + for path in ei_cmd.filelist.files: + d, f = os.path.split(assert_relative(path)) + prev = None + oldf = f + while d and d != prev and d not in src_dirs: + prev = d + d, df = os.path.split(d) + f = os.path.join(df, f) + if d in src_dirs: + if path.endswith('.py') and f == oldf: + continue # it's a module, not data + mf.setdefault(src_dirs[d], []).append(path) + + def get_data_files(self): + pass # Lazily compute data files in _get_data_files() function. + + def check_package(self, package, package_dir): + """Check namespace packages' __init__ for declare_namespace""" + try: + return self.packages_checked[package] + except KeyError: + pass + + init_py = orig.build_py.check_package(self, package, package_dir) + self.packages_checked[package] = init_py + + if not init_py or not self.distribution.namespace_packages: + return init_py + + for pkg in self.distribution.namespace_packages: + if pkg == package or pkg.startswith(package + '.'): + break + else: + return init_py + + with io.open(init_py, 'rb') as f: + contents = f.read() + if b'declare_namespace' not in contents: + raise distutils.errors.DistutilsError( + "Namespace package problem: %s is a namespace package, but " + "its\n__init__.py does not call declare_namespace()! Please " + 'fix it.\n(See the setuptools manual under ' + '"Namespace Packages" for details.)\n"' % (package,) + ) + return init_py + + def initialize_options(self): + self.packages_checked = {} + orig.build_py.initialize_options(self) + + def get_package_dir(self, package): + res = orig.build_py.get_package_dir(self, package) + if self.distribution.src_root is not None: + return os.path.join(self.distribution.src_root, res) + return res + + def exclude_data_files(self, package, src_dir, files): + """Filter filenames for package's data files in 'src_dir'""" + globs = ( + self.exclude_package_data.get('', []) + + self.exclude_package_data.get(package, []) + ) + bad = set( + item + for pattern in globs + for item in fnmatch.filter( + files, + os.path.join(src_dir, convert_path(pattern)), + ) + ) + seen = collections.defaultdict(itertools.count) + return [ + fn + for fn in files + if fn not in bad + # ditch dupes + and not next(seen[fn]) + ] + + +def assert_relative(path): + if not os.path.isabs(path): + return path + from distutils.errors import DistutilsSetupError + + msg = textwrap.dedent(""" + Error: setup script specifies an absolute path: + + %s + + setup() arguments must *always* be /-separated paths relative to the + setup.py directory, *never* absolute paths. + """).lstrip() % path + raise DistutilsSetupError(msg) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/develop.py b/venv/lib/python3.5/site-packages/setuptools/command/develop.py new file mode 100644 index 0000000..11b5df1 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/develop.py @@ -0,0 +1,196 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsError, DistutilsOptionError +import os +import glob +import io + +from setuptools.extern import six + +from pkg_resources import Distribution, PathMetadata, normalize_path +from setuptools.command.easy_install import easy_install +import setuptools + + +class develop(easy_install): + """Set up package for development""" + + description = "install package in 'development mode'" + + user_options = easy_install.user_options + [ + ("uninstall", "u", "Uninstall this source package"), + ("egg-path=", None, "Set the path to be used in the .egg-link file"), + ] + + boolean_options = easy_install.boolean_options + ['uninstall'] + + command_consumes_arguments = False # override base + + def run(self): + if self.uninstall: + self.multi_version = True + self.uninstall_link() + else: + self.install_for_development() + self.warn_deprecated_options() + + def initialize_options(self): + self.uninstall = None + self.egg_path = None + easy_install.initialize_options(self) + self.setup_path = None + self.always_copy_from = '.' # always copy eggs installed in curdir + + def finalize_options(self): + ei = self.get_finalized_command("egg_info") + if ei.broken_egg_info: + template = "Please rename %r to %r before using 'develop'" + args = ei.egg_info, ei.broken_egg_info + raise DistutilsError(template % args) + self.args = [ei.egg_name] + + easy_install.finalize_options(self) + self.expand_basedirs() + self.expand_dirs() + # pick up setup-dir .egg files only: no .egg-info + self.package_index.scan(glob.glob('*.egg')) + + egg_link_fn = ei.egg_name + '.egg-link' + self.egg_link = os.path.join(self.install_dir, egg_link_fn) + self.egg_base = ei.egg_base + if self.egg_path is None: + self.egg_path = os.path.abspath(ei.egg_base) + + target = normalize_path(self.egg_base) + egg_path = normalize_path(os.path.join(self.install_dir, + self.egg_path)) + if egg_path != target: + raise DistutilsOptionError( + "--egg-path must be a relative path from the install" + " directory to " + target + ) + + # Make a distribution for the package's source + self.dist = Distribution( + target, + PathMetadata(target, os.path.abspath(ei.egg_info)), + project_name=ei.egg_name + ) + + p = self.egg_base.replace(os.sep, '/') + if p != os.curdir: + p = '../' * (p.count('/') + 1) + self.setup_path = p + p = normalize_path(os.path.join(self.install_dir, self.egg_path, p)) + if p != normalize_path(os.curdir): + raise DistutilsOptionError( + "Can't get a consistent path to setup script from" + " installation directory", p, normalize_path(os.curdir)) + + def install_for_development(self): + if six.PY3 and getattr(self.distribution, 'use_2to3', False): + # If we run 2to3 we can not do this inplace: + + # Ensure metadata is up-to-date + self.reinitialize_command('build_py', inplace=0) + self.run_command('build_py') + bpy_cmd = self.get_finalized_command("build_py") + build_path = normalize_path(bpy_cmd.build_lib) + + # Build extensions + self.reinitialize_command('egg_info', egg_base=build_path) + self.run_command('egg_info') + + self.reinitialize_command('build_ext', inplace=0) + self.run_command('build_ext') + + # Fixup egg-link and easy-install.pth + ei_cmd = self.get_finalized_command("egg_info") + self.egg_path = build_path + self.dist.location = build_path + # XXX + self.dist._provider = PathMetadata(build_path, ei_cmd.egg_info) + else: + # Without 2to3 inplace works fine: + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + self.install_site_py() # ensure that target dir is site-safe + if setuptools.bootstrap_install_from: + self.easy_install(setuptools.bootstrap_install_from) + setuptools.bootstrap_install_from = None + + # create an .egg-link in the installation dir, pointing to our egg + log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) + if not self.dry_run: + with open(self.egg_link, "w") as f: + f.write(self.egg_path + "\n" + self.setup_path) + # postprocess the installed distro, fixing up .pth, installing scripts, + # and handling requirements + self.process_distribution(None, self.dist, not self.no_deps) + + def uninstall_link(self): + if os.path.exists(self.egg_link): + log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) + egg_link_file = open(self.egg_link) + contents = [line.rstrip() for line in egg_link_file] + egg_link_file.close() + if contents not in ([self.egg_path], + [self.egg_path, self.setup_path]): + log.warn("Link points to %s: uninstall aborted", contents) + return + if not self.dry_run: + os.unlink(self.egg_link) + if not self.dry_run: + self.update_pth(self.dist) # remove any .pth link to us + if self.distribution.scripts: + # XXX should also check for entry point scripts! + log.warn("Note: you must uninstall or replace scripts manually!") + + def install_egg_scripts(self, dist): + if dist is not self.dist: + # Installing a dependency, so fall back to normal behavior + return easy_install.install_egg_scripts(self, dist) + + # create wrapper scripts in the script dir, pointing to dist.scripts + + # new-style... + self.install_wrapper_scripts(dist) + + # ...and old-style + for script_name in self.distribution.scripts or []: + script_path = os.path.abspath(convert_path(script_name)) + script_name = os.path.basename(script_path) + with io.open(script_path) as strm: + script_text = strm.read() + self.install_script(dist, script_name, script_text, script_path) + + def install_wrapper_scripts(self, dist): + dist = VersionlessRequirement(dist) + return easy_install.install_wrapper_scripts(self, dist) + + +class VersionlessRequirement(object): + """ + Adapt a pkg_resources.Distribution to simply return the project + name as the 'requirement' so that scripts will work across + multiple versions. + + >>> dist = Distribution(project_name='foo', version='1.0') + >>> str(dist.as_requirement()) + 'foo==1.0' + >>> adapted_dist = VersionlessRequirement(dist) + >>> str(adapted_dist.as_requirement()) + 'foo' + """ + def __init__(self, dist): + self.__dist = dist + + def __getattr__(self, name): + return getattr(self.__dist, name) + + def as_requirement(self): + return self.project_name diff --git a/venv/lib/python3.5/site-packages/setuptools/command/easy_install.py b/venv/lib/python3.5/site-packages/setuptools/command/easy_install.py new file mode 100644 index 0000000..7683e3b --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/easy_install.py @@ -0,0 +1,2315 @@ +#!/usr/bin/env python + +""" +Easy Install +------------ + +A tool for doing automatic download/extract/build of distutils-based Python +packages. For detailed documentation, see the accompanying EasyInstall.txt +file, or visit the `EasyInstall home page`__. + +__ https://pythonhosted.org/setuptools/easy_install.html + +""" + +from glob import glob +from distutils.util import get_platform +from distutils.util import convert_path, subst_vars +from distutils.errors import DistutilsArgError, DistutilsOptionError, \ + DistutilsError, DistutilsPlatformError +from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS +from distutils import log, dir_util +from distutils.command.build_scripts import first_line_re +from distutils.spawn import find_executable +import sys +import os +import zipimport +import shutil +import tempfile +import zipfile +import re +import stat +import random +import platform +import textwrap +import warnings +import site +import struct +import contextlib +import subprocess +import shlex +import io + +from setuptools.extern import six +from setuptools.extern.six.moves import configparser, map + +from setuptools import Command +from setuptools.sandbox import run_setup +from setuptools.py31compat import get_path, get_config_vars +from setuptools.command import setopt +from setuptools.archive_util import unpack_archive +from setuptools.package_index import PackageIndex +from setuptools.package_index import URL_SCHEME +from setuptools.command import bdist_egg, egg_info +from pkg_resources import ( + yield_lines, normalize_path, resource_string, ensure_directory, + get_distribution, find_distributions, Environment, Requirement, + Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, + VersionConflict, DEVELOP_DIST, +) +import pkg_resources + +# Turn on PEP440Warnings +warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) + + +__all__ = [ + 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', + 'main', 'get_exe_prefixes', +] + + +def is_64bit(): + return struct.calcsize("P") == 8 + + +def samefile(p1, p2): + both_exist = os.path.exists(p1) and os.path.exists(p2) + use_samefile = hasattr(os.path, 'samefile') and both_exist + if use_samefile: + return os.path.samefile(p1, p2) + norm_p1 = os.path.normpath(os.path.normcase(p1)) + norm_p2 = os.path.normpath(os.path.normcase(p2)) + return norm_p1 == norm_p2 + + +if six.PY2: + def _to_ascii(s): + return s + + def isascii(s): + try: + six.text_type(s, 'ascii') + return True + except UnicodeError: + return False +else: + def _to_ascii(s): + return s.encode('ascii') + + def isascii(s): + try: + s.encode('ascii') + return True + except UnicodeError: + return False + + +class easy_install(Command): + """Manage a download/build/install process""" + description = "Find/get/install Python packages" + command_consumes_arguments = True + + user_options = [ + ('prefix=', None, "installation prefix"), + ("zip-ok", "z", "install package as a zipfile"), + ("multi-version", "m", "make apps have to require() a version"), + ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"), + ("install-dir=", "d", "install package to DIR"), + ("script-dir=", "s", "install scripts to DIR"), + ("exclude-scripts", "x", "Don't install scripts"), + ("always-copy", "a", "Copy all needed packages to install dir"), + ("index-url=", "i", "base URL of Python Package Index"), + ("find-links=", "f", "additional URL(s) to search for packages"), + ("build-directory=", "b", + "download/extract/build in DIR; keep the results"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('record=', None, + "filename in which to record list of installed files"), + ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), + ('site-dirs=', 'S', "list of directories where .pth files work"), + ('editable', 'e', "Install specified packages in editable form"), + ('no-deps', 'N', "don't install dependencies"), + ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), + ('local-snapshots-ok', 'l', + "allow building eggs from local checkouts"), + ('version', None, "print version information and exit"), + ('install-layout=', None, "installation layout to choose (known values: deb)"), + ('force-installation-into-system-dir', '0', "force installation into /usr"), + ('no-find-links', None, + "Don't load find-links defined in packages being installed") + ] + boolean_options = [ + 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', + 'editable', + 'no-deps', 'local-snapshots-ok', 'version', 'force-installation-into-system-dir' + ] + + if site.ENABLE_USER_SITE: + help_msg = "install in user site-package '%s'" % site.USER_SITE + user_options.append(('user', None, help_msg)) + boolean_options.append('user') + + negative_opt = {'always-unzip': 'zip-ok'} + create_index = PackageIndex + + def initialize_options(self): + # the --user option seems to be an opt-in one, + # so the default should be False. + self.user = 0 + self.zip_ok = self.local_snapshots_ok = None + self.install_dir = self.script_dir = self.exclude_scripts = None + self.index_url = None + self.find_links = None + self.build_directory = None + self.args = None + self.optimize = self.record = None + self.upgrade = self.always_copy = self.multi_version = None + self.editable = self.no_deps = self.allow_hosts = None + self.root = self.prefix = self.no_report = None + self.version = None + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers + self.install_lib = None # set to either purelib or platlib + self.install_scripts = None + self.install_data = None + self.install_base = None + self.install_platbase = None + if site.ENABLE_USER_SITE: + self.install_userbase = site.USER_BASE + self.install_usersite = site.USER_SITE + else: + self.install_userbase = None + self.install_usersite = None + self.no_find_links = None + + # Options not specifiable via command line + self.package_index = None + self.pth_file = self.always_copy_from = None + self.site_dirs = None + self.installed_projects = {} + self.sitepy_installed = False + # enable custom installation, known values: deb + self.install_layout = None + self.force_installation_into_system_dir = None + self.multiarch = None + + # Always read easy_install options, even if we are subclassed, or have + # an independent instance created. This ensures that defaults will + # always come from the standard configuration file(s)' "easy_install" + # section, even if this is a "develop" or "install" command, or some + # other embedding. + self._dry_run = None + self.verbose = self.distribution.verbose + self.distribution._set_command_options( + self, self.distribution.get_option_dict('easy_install') + ) + + def delete_blockers(self, blockers): + extant_blockers = ( + filename for filename in blockers + if os.path.exists(filename) or os.path.islink(filename) + ) + list(map(self._delete_path, extant_blockers)) + + def _delete_path(self, path): + log.info("Deleting %s", path) + if self.dry_run: + return + + is_tree = os.path.isdir(path) and not os.path.islink(path) + remover = rmtree if is_tree else os.unlink + remover(path) + + @staticmethod + def _render_version(): + """ + Render the Setuptools version and installation details, then exit. + """ + ver = sys.version[:3] + dist = get_distribution('setuptools') + tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})' + print(tmpl.format(**locals())) + raise SystemExit() + + def finalize_options(self): + self.version and self._render_version() + + py_version = sys.version.split()[0] + prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix') + + self.config_vars = { + 'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': py_version[0:3], + 'py_version_nodot': py_version[0] + py_version[2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + # Only python 3.2+ has abiflags + 'abiflags': getattr(sys, 'abiflags', ''), + } + + if site.ENABLE_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + + self._fix_install_dir_for_user_site() + + self.expand_basedirs() + self.expand_dirs() + + if self.install_layout: + if not self.install_layout.lower() in ['deb']: + raise DistutilsOptionError("unknown value for --install-layout") + self.install_layout = self.install_layout.lower() + import sysconfig + if sys.version_info[:2] >= (3, 3): + self.multiarch = sysconfig.get_config_var('MULTIARCH') + self._expand('install_dir', 'script_dir', 'build_directory', + 'site_dirs') + # If a non-default installation directory was specified, default the + # script directory to match it. + if self.script_dir is None: + self.script_dir = self.install_dir + + if self.no_find_links is None: + self.no_find_links = False + + # Let install_dir get set by install_lib command, which in turn + # gets its info from the install command, and takes into account + # --prefix and --home and all that other crud. + self.set_undefined_options( + 'install_lib', ('install_dir', 'install_dir') + ) + # Likewise, set default script_dir from 'install_scripts.install_dir' + self.set_undefined_options( + 'install_scripts', ('install_dir', 'script_dir') + ) + + if self.user and self.install_purelib: + self.install_dir = self.install_purelib + self.script_dir = self.install_scripts + + if self.prefix == '/usr' and not self.force_installation_into_system_dir: + raise DistutilsOptionError("""installation into /usr + +Trying to install into the system managed parts of the file system. Please +consider to install to another location, or use the option +--force-installation-into-system-dir to overwrite this warning. +""") + + # default --record from the install command + self.set_undefined_options('install', ('record', 'record')) + # Should this be moved to the if statement below? It's not used + # elsewhere + normpath = map(normalize_path, sys.path) + self.all_site_dirs = get_site_dirs() + if self.site_dirs is not None: + site_dirs = [ + os.path.expanduser(s.strip()) for s in + self.site_dirs.split(',') + ] + for d in site_dirs: + if not os.path.isdir(d): + log.warn("%s (in --site-dirs) does not exist", d) + elif normalize_path(d) not in normpath: + raise DistutilsOptionError( + d + " (in --site-dirs) is not on sys.path" + ) + else: + self.all_site_dirs.append(normalize_path(d)) + if not self.editable: + self.check_site_dir() + self.index_url = self.index_url or "https://pypi.python.org/simple" + self.shadow_path = self.all_site_dirs[:] + for path_item in self.install_dir, normalize_path(self.script_dir): + if path_item not in self.shadow_path: + self.shadow_path.insert(0, path_item) + + if self.allow_hosts is not None: + hosts = [s.strip() for s in self.allow_hosts.split(',')] + else: + hosts = ['*'] + if self.package_index is None: + self.package_index = self.create_index( + self.index_url, search_path=self.shadow_path, hosts=hosts, + ) + self.local_index = Environment(self.shadow_path + sys.path) + + if self.find_links is not None: + if isinstance(self.find_links, six.string_types): + self.find_links = self.find_links.split() + else: + self.find_links = [] + if self.local_snapshots_ok: + self.package_index.scan_egg_links(self.shadow_path + sys.path) + if not self.no_find_links: + self.package_index.add_find_links(self.find_links) + self.set_undefined_options('install_lib', ('optimize', 'optimize')) + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + if not (0 <= self.optimize <= 2): + raise ValueError + except ValueError: + raise DistutilsOptionError("--optimize must be 0, 1, or 2") + + if self.editable and not self.build_directory: + raise DistutilsArgError( + "Must specify a build directory (-b) when using --editable" + ) + if not self.args: + raise DistutilsArgError( + "No urls, filenames, or requirements specified (see --help)") + + self.outputs = [] + + def _fix_install_dir_for_user_site(self): + """ + Fix the install_dir if "--user" was used. + """ + if not self.user or not site.ENABLE_USER_SITE: + return + + self.create_home_path() + if self.install_userbase is None: + msg = "User base directory is not specified" + raise DistutilsPlatformError(msg) + self.install_base = self.install_platbase = self.install_userbase + scheme_name = os.name.replace('posix', 'unix') + '_user' + self.select_scheme(scheme_name) + + def _expand_attrs(self, attrs): + for attr in attrs: + val = getattr(self, attr) + if val is not None: + if os.name == 'posix' or os.name == 'nt': + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) + + def expand_basedirs(self): + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) + + def expand_dirs(self): + """Calls `os.path.expanduser` on install dirs.""" + self._expand_attrs(['install_purelib', 'install_platlib', + 'install_lib', 'install_headers', + 'install_scripts', 'install_data', ]) + + def run(self): + if self.verbose != self.distribution.verbose: + log.set_verbosity(self.verbose) + try: + for spec in self.args: + self.easy_install(spec, not self.no_deps) + if self.record: + outputs = self.outputs + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in range(len(outputs)): + outputs[counter] = outputs[counter][root_len:] + from distutils import file_util + + self.execute( + file_util.write_file, (self.record, outputs), + "writing list of installed files to '%s'" % + self.record + ) + self.warn_deprecated_options() + finally: + log.set_verbosity(self.distribution.verbose) + + def pseudo_tempname(self): + """Return a pseudo-tempname base in the install directory. + This code is intentionally naive; if a malicious party can write to + the target directory you're already in deep doodoo. + """ + try: + pid = os.getpid() + except: + pid = random.randint(0, sys.maxsize) + return os.path.join(self.install_dir, "test-easy-install-%s" % pid) + + def warn_deprecated_options(self): + pass + + def check_site_dir(self): + """Verify that self.install_dir is .pth-capable dir, if needed""" + + instdir = normalize_path(self.install_dir) + pth_file = os.path.join(instdir, 'easy-install.pth') + + # Is it a configured, PYTHONPATH, implicit, or explicit site dir? + is_site_dir = instdir in self.all_site_dirs + + if not is_site_dir and not self.multi_version: + # No? Then directly test whether it does .pth file processing + is_site_dir = self.check_pth_processing() + else: + # make sure we can write to target dir + testfile = self.pseudo_tempname() + '.write-test' + test_exists = os.path.exists(testfile) + try: + if test_exists: + os.unlink(testfile) + open(testfile, 'w').close() + os.unlink(testfile) + except (OSError, IOError): + self.cant_write_to_target() + + if not is_site_dir and not self.multi_version: + # Can't install non-multi to non-site dir + raise DistutilsError(self.no_default_version_msg()) + + if is_site_dir: + if self.pth_file is None: + self.pth_file = PthDistributions(pth_file, self.all_site_dirs) + else: + self.pth_file = None + + PYTHONPATH = os.environ.get('PYTHONPATH', '').split(os.pathsep) + if instdir not in map(normalize_path, filter(None, PYTHONPATH)): + # only PYTHONPATH dirs need a site.py, so pretend it's there + self.sitepy_installed = True + elif self.multi_version and not os.path.exists(pth_file): + self.sitepy_installed = True # don't need site.py in this case + self.pth_file = None # and don't create a .pth file + self.install_dir = instdir + + __cant_write_msg = textwrap.dedent(""" + can't create or remove files in install directory + + The following error occurred while trying to add or remove files in the + installation directory: + + %s + + The installation directory you specified (via --install-dir, --prefix, or + the distutils default setting) was: + + %s + """).lstrip() + + __not_exists_id = textwrap.dedent(""" + This directory does not currently exist. Please create it and try again, or + choose a different installation directory (using the -d or --install-dir + option). + """).lstrip() + + __access_msg = textwrap.dedent(""" + Perhaps your account does not have write access to this directory? If the + installation directory is a system-owned directory, you may need to sign in + as the administrator or "root" account. If you do not have administrative + access to this machine, you may wish to choose a different installation + directory, preferably one that is listed in your PYTHONPATH environment + variable. + + For information on other options, you may wish to consult the + documentation at: + + https://pythonhosted.org/setuptools/easy_install.html + + Please make the appropriate changes for your system and try again. + """).lstrip() + + def cant_write_to_target(self): + msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,) + + if not os.path.exists(self.install_dir): + msg += '\n' + self.__not_exists_id + else: + msg += '\n' + self.__access_msg + raise DistutilsError(msg) + + def check_pth_processing(self): + """Empirically verify whether .pth files are supported in inst. dir""" + instdir = self.install_dir + log.info("Checking .pth file support in %s", instdir) + pth_file = self.pseudo_tempname() + ".pth" + ok_file = pth_file + '.ok' + ok_exists = os.path.exists(ok_file) + try: + if ok_exists: + os.unlink(ok_file) + dirname = os.path.dirname(ok_file) + if not os.path.exists(dirname): + os.makedirs(dirname) + f = open(pth_file, 'w') + except (OSError, IOError): + self.cant_write_to_target() + else: + try: + f.write("import os; f = open(%r, 'w'); f.write('OK'); " + "f.close()\n" % (ok_file,)) + f.close() + f = None + executable = sys.executable + if os.name == 'nt': + dirname, basename = os.path.split(executable) + alt = os.path.join(dirname, 'pythonw.exe') + if (basename.lower() == 'python.exe' and + os.path.exists(alt)): + # use pythonw.exe to avoid opening a console window + executable = alt + + from distutils.spawn import spawn + + spawn([executable, '-E', '-c', 'pass'], 0) + + if os.path.exists(ok_file): + log.info( + "TEST PASSED: %s appears to support .pth files", + instdir + ) + return True + finally: + if f: + f.close() + if os.path.exists(ok_file): + os.unlink(ok_file) + if os.path.exists(pth_file): + os.unlink(pth_file) + if not self.multi_version: + log.warn("TEST FAILED: %s does NOT support .pth files", instdir) + return False + + def install_egg_scripts(self, dist): + """Write all the scripts for `dist`, unless scripts are excluded""" + if not self.exclude_scripts and dist.metadata_isdir('scripts'): + for script_name in dist.metadata_listdir('scripts'): + if dist.metadata_isdir('scripts/' + script_name): + # The "script" is a directory, likely a Python 3 + # __pycache__ directory, so skip it. + continue + self.install_script( + dist, script_name, + dist.get_metadata('scripts/' + script_name) + ) + self.install_wrapper_scripts(dist) + + def add_output(self, path): + if os.path.isdir(path): + for base, dirs, files in os.walk(path): + for filename in files: + self.outputs.append(os.path.join(base, filename)) + else: + self.outputs.append(path) + + def not_editable(self, spec): + if self.editable: + raise DistutilsArgError( + "Invalid argument %r: you can't use filenames or URLs " + "with --editable (except via the --find-links option)." + % (spec,) + ) + + def check_editable(self, spec): + if not self.editable: + return + + if os.path.exists(os.path.join(self.build_directory, spec.key)): + raise DistutilsArgError( + "%r already exists in %s; can't do a checkout there" % + (spec.key, self.build_directory) + ) + + def easy_install(self, spec, deps=False): + tmpdir = tempfile.mkdtemp(prefix="easy_install-") + download = None + if not self.editable: + self.install_site_py() + + try: + if not isinstance(spec, Requirement): + if URL_SCHEME(spec): + # It's a url, download it to tmpdir and process + self.not_editable(spec) + download = self.package_index.download(spec, tmpdir) + return self.install_item(None, download, tmpdir, deps, + True) + + elif os.path.exists(spec): + # Existing file or directory, just process it directly + self.not_editable(spec) + return self.install_item(None, spec, tmpdir, deps, True) + else: + spec = parse_requirement_arg(spec) + + self.check_editable(spec) + dist = self.package_index.fetch_distribution( + spec, tmpdir, self.upgrade, self.editable, + not self.always_copy, self.local_index + ) + if dist is None: + msg = "Could not find suitable distribution for %r" % spec + if self.always_copy: + msg += " (--always-copy skips system and development eggs)" + raise DistutilsError(msg) + elif dist.precedence == DEVELOP_DIST: + # .egg-info dists don't need installing, just process deps + self.process_distribution(spec, dist, deps, "Using") + return dist + else: + return self.install_item(spec, dist.location, tmpdir, deps) + + finally: + if os.path.exists(tmpdir): + rmtree(tmpdir) + + def install_item(self, spec, download, tmpdir, deps, install_needed=False): + + # Installation is also needed if file in tmpdir or is not an egg + install_needed = install_needed or self.always_copy + install_needed = install_needed or os.path.dirname(download) == tmpdir + install_needed = install_needed or not download.endswith('.egg') + install_needed = install_needed or ( + self.always_copy_from is not None and + os.path.dirname(normalize_path(download)) == + normalize_path(self.always_copy_from) + ) + + if spec and not install_needed: + # at this point, we know it's a local .egg, we just don't know if + # it's already installed. + for dist in self.local_index[spec.project_name]: + if dist.location == download: + break + else: + install_needed = True # it's not in the local index + + log.info("Processing %s", os.path.basename(download)) + + if install_needed: + dists = self.install_eggs(spec, download, tmpdir) + for dist in dists: + self.process_distribution(spec, dist, deps) + else: + dists = [self.egg_distribution(download)] + self.process_distribution(spec, dists[0], deps, "Using") + + if spec is not None: + for dist in dists: + if dist in spec: + return dist + + def select_scheme(self, name): + """Sets the install directories by applying the install schemes.""" + # it's the caller's problem if they supply a bad name! + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: + attrname = 'install_' + key + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) + + def process_distribution(self, requirement, dist, deps=True, *info): + self.update_pth(dist) + self.package_index.add(dist) + if dist in self.local_index[dist.key]: + self.local_index.remove(dist) + self.local_index.add(dist) + self.install_egg_scripts(dist) + self.installed_projects[dist.key] = dist + log.info(self.installation_report(requirement, dist, *info)) + if (dist.has_metadata('dependency_links.txt') and + not self.no_find_links): + self.package_index.add_find_links( + dist.get_metadata_lines('dependency_links.txt') + ) + if not deps and not self.always_copy: + return + elif requirement is not None and dist.key != requirement.key: + log.warn("Skipping dependencies for %s", dist) + return # XXX this is not the distribution we were looking for + elif requirement is None or dist not in requirement: + # if we wound up with a different version, resolve what we've got + distreq = dist.as_requirement() + requirement = Requirement(str(distreq)) + log.info("Processing dependencies for %s", requirement) + try: + distros = WorkingSet([]).resolve( + [requirement], self.local_index, self.easy_install + ) + except DistributionNotFound as e: + raise DistutilsError(str(e)) + except VersionConflict as e: + raise DistutilsError(e.report()) + if self.always_copy or self.always_copy_from: + # Force all the relevant distros to be copied or activated + for dist in distros: + if dist.key not in self.installed_projects: + self.easy_install(dist.as_requirement()) + log.info("Finished processing dependencies for %s", requirement) + + def should_unzip(self, dist): + if self.zip_ok is not None: + return not self.zip_ok + if dist.has_metadata('not-zip-safe'): + return True + if not dist.has_metadata('zip-safe'): + return True + return False + + def maybe_move(self, spec, dist_filename, setup_base): + dst = os.path.join(self.build_directory, spec.key) + if os.path.exists(dst): + msg = ("%r already exists in %s; build directory %s will not be " + "kept") + log.warn(msg, spec.key, self.build_directory, setup_base) + return setup_base + if os.path.isdir(dist_filename): + setup_base = dist_filename + else: + if os.path.dirname(dist_filename) == setup_base: + os.unlink(dist_filename) # get it out of the tmp dir + contents = os.listdir(setup_base) + if len(contents) == 1: + dist_filename = os.path.join(setup_base, contents[0]) + if os.path.isdir(dist_filename): + # if the only thing there is a directory, move it instead + setup_base = dist_filename + ensure_directory(dst) + shutil.move(setup_base, dst) + return dst + + def install_wrapper_scripts(self, dist): + if self.exclude_scripts: + return + for args in ScriptWriter.best().get_args(dist): + self.write_script(*args) + + def install_script(self, dist, script_name, script_text, dev_path=None): + """Generate a legacy script wrapper and install it""" + spec = str(dist.as_requirement()) + is_script = is_python_script(script_text, script_name) + + if is_script: + body = self._load_template(dev_path) % locals() + script_text = ScriptWriter.get_header(script_text) + body + self.write_script(script_name, _to_ascii(script_text), 'b') + + @staticmethod + def _load_template(dev_path): + """ + There are a couple of template scripts in the package. This + function loads one of them and prepares it for use. + """ + # See https://github.com/pypa/setuptools/issues/134 for info + # on script file naming and downstream issues with SVR4 + name = 'script.tmpl' + if dev_path: + name = name.replace('.tmpl', ' (dev).tmpl') + + raw_bytes = resource_string('setuptools', name) + return raw_bytes.decode('utf-8') + + def write_script(self, script_name, contents, mode="t", blockers=()): + """Write an executable file to the scripts directory""" + self.delete_blockers( # clean up old .py/.pyw w/o a script + [os.path.join(self.script_dir, x) for x in blockers] + ) + log.info("Installing %s script to %s", script_name, self.script_dir) + target = os.path.join(self.script_dir, script_name) + self.add_output(target) + + mask = current_umask() + if not self.dry_run: + ensure_directory(target) + if os.path.exists(target): + os.unlink(target) + with open(target, "w" + mode) as f: + f.write(contents) + chmod(target, 0o777 - mask) + + def install_eggs(self, spec, dist_filename, tmpdir): + # .egg dirs or files are already built, so just return them + if dist_filename.lower().endswith('.egg'): + return [self.install_egg(dist_filename, tmpdir)] + elif dist_filename.lower().endswith('.exe'): + return [self.install_exe(dist_filename, tmpdir)] + + # Anything else, try to extract and build + setup_base = tmpdir + if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'): + unpack_archive(dist_filename, tmpdir, self.unpack_progress) + elif os.path.isdir(dist_filename): + setup_base = os.path.abspath(dist_filename) + + if (setup_base.startswith(tmpdir) # something we downloaded + and self.build_directory and spec is not None): + setup_base = self.maybe_move(spec, dist_filename, setup_base) + + # Find the setup.py file + setup_script = os.path.join(setup_base, 'setup.py') + + if not os.path.exists(setup_script): + setups = glob(os.path.join(setup_base, '*', 'setup.py')) + if not setups: + raise DistutilsError( + "Couldn't find a setup script in %s" % + os.path.abspath(dist_filename) + ) + if len(setups) > 1: + raise DistutilsError( + "Multiple setup scripts in %s" % + os.path.abspath(dist_filename) + ) + setup_script = setups[0] + + # Now run it, and return the result + if self.editable: + log.info(self.report_editable(spec, setup_script)) + return [] + else: + return self.build_and_install(setup_script, setup_base) + + def egg_distribution(self, egg_path): + if os.path.isdir(egg_path): + metadata = PathMetadata(egg_path, os.path.join(egg_path, + 'EGG-INFO')) + else: + metadata = EggMetadata(zipimport.zipimporter(egg_path)) + return Distribution.from_filename(egg_path, metadata=metadata) + + def install_egg(self, egg_path, tmpdir): + destination = os.path.join(self.install_dir, + os.path.basename(egg_path)) + destination = os.path.abspath(destination) + if not self.dry_run: + ensure_directory(destination) + + dist = self.egg_distribution(egg_path) + if not samefile(egg_path, destination): + if os.path.isdir(destination) and not os.path.islink(destination): + dir_util.remove_tree(destination, dry_run=self.dry_run) + elif os.path.exists(destination): + self.execute(os.unlink, (destination,), "Removing " + + destination) + try: + new_dist_is_zipped = False + if os.path.isdir(egg_path): + if egg_path.startswith(tmpdir): + f, m = shutil.move, "Moving" + else: + f, m = shutil.copytree, "Copying" + elif self.should_unzip(dist): + self.mkpath(destination) + f, m = self.unpack_and_compile, "Extracting" + else: + new_dist_is_zipped = True + if egg_path.startswith(tmpdir): + f, m = shutil.move, "Moving" + else: + f, m = shutil.copy2, "Copying" + self.execute(f, (egg_path, destination), + (m + " %s to %s") % + (os.path.basename(egg_path), + os.path.dirname(destination))) + update_dist_caches(destination, + fix_zipimporter_caches=new_dist_is_zipped) + except: + update_dist_caches(destination, fix_zipimporter_caches=False) + raise + + self.add_output(destination) + return self.egg_distribution(destination) + + def install_exe(self, dist_filename, tmpdir): + # See if it's valid, get data + cfg = extract_wininst_cfg(dist_filename) + if cfg is None: + raise DistutilsError( + "%s is not a valid distutils Windows .exe" % dist_filename + ) + # Create a dummy distribution object until we build the real distro + dist = Distribution( + None, + project_name=cfg.get('metadata', 'name'), + version=cfg.get('metadata', 'version'), platform=get_platform(), + ) + + # Convert the .exe to an unpacked egg + egg_path = dist.location = os.path.join(tmpdir, dist.egg_name() + + '.egg') + egg_tmp = egg_path + '.tmp' + _egg_info = os.path.join(egg_tmp, 'EGG-INFO') + pkg_inf = os.path.join(_egg_info, 'PKG-INFO') + ensure_directory(pkg_inf) # make sure EGG-INFO dir exists + dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX + self.exe_to_egg(dist_filename, egg_tmp) + + # Write EGG-INFO/PKG-INFO + if not os.path.exists(pkg_inf): + f = open(pkg_inf, 'w') + f.write('Metadata-Version: 1.0\n') + for k, v in cfg.items('metadata'): + if k != 'target_version': + f.write('%s: %s\n' % (k.replace('_', '-').title(), v)) + f.close() + script_dir = os.path.join(_egg_info, 'scripts') + # delete entry-point scripts to avoid duping + self.delete_blockers( + [os.path.join(script_dir, args[0]) for args in + ScriptWriter.get_args(dist)] + ) + # Build .egg file from tmpdir + bdist_egg.make_zipfile( + egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run + ) + # install the .egg + return self.install_egg(egg_path, tmpdir) + + def exe_to_egg(self, dist_filename, egg_tmp): + """Extract a bdist_wininst to the directories an egg would use""" + # Check for .pth file and set up prefix translations + prefixes = get_exe_prefixes(dist_filename) + to_compile = [] + native_libs = [] + top_level = {} + + def process(src, dst): + s = src.lower() + for old, new in prefixes: + if s.startswith(old): + src = new + src[len(old):] + parts = src.split('/') + dst = os.path.join(egg_tmp, *parts) + dl = dst.lower() + if dl.endswith('.pyd') or dl.endswith('.dll'): + parts[-1] = bdist_egg.strip_module(parts[-1]) + top_level[os.path.splitext(parts[0])[0]] = 1 + native_libs.append(src) + elif dl.endswith('.py') and old != 'SCRIPTS/': + top_level[os.path.splitext(parts[0])[0]] = 1 + to_compile.append(dst) + return dst + if not src.endswith('.pth'): + log.warn("WARNING: can't process %s", src) + return None + + # extract, tracking .pyd/.dll->native_libs and .py -> to_compile + unpack_archive(dist_filename, egg_tmp, process) + stubs = [] + for res in native_libs: + if res.lower().endswith('.pyd'): # create stubs for .pyd's + parts = res.split('/') + resource = parts[-1] + parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py' + pyfile = os.path.join(egg_tmp, *parts) + to_compile.append(pyfile) + stubs.append(pyfile) + bdist_egg.write_stub(resource, pyfile) + self.byte_compile(to_compile) # compile .py's + bdist_egg.write_safety_flag( + os.path.join(egg_tmp, 'EGG-INFO'), + bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag + + for name in 'top_level', 'native_libs': + if locals()[name]: + txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt') + if not os.path.exists(txt): + f = open(txt, 'w') + f.write('\n'.join(locals()[name]) + '\n') + f.close() + + __mv_warning = textwrap.dedent(""" + Because this distribution was installed --multi-version, before you can + import modules from this package in an application, you will need to + 'import pkg_resources' and then use a 'require()' call similar to one of + these examples, in order to select the desired version: + + pkg_resources.require("%(name)s") # latest installed version + pkg_resources.require("%(name)s==%(version)s") # this exact version + pkg_resources.require("%(name)s>=%(version)s") # this version or higher + """).lstrip() + + __id_warning = textwrap.dedent(""" + Note also that the installation directory must be on sys.path at runtime for + this to work. (e.g. by being the application's script directory, by being on + PYTHONPATH, or by being added to sys.path by your code.) + """) + + def installation_report(self, req, dist, what="Installed"): + """Helpful installation message for display to package users""" + msg = "\n%(what)s %(eggloc)s%(extras)s" + if self.multi_version and not self.no_report: + msg += '\n' + self.__mv_warning + if self.install_dir not in map(normalize_path, sys.path): + msg += '\n' + self.__id_warning + + eggloc = dist.location + name = dist.project_name + version = dist.version + extras = '' # TODO: self.report_extras(req, dist) + return msg % locals() + + __editable_msg = textwrap.dedent(""" + Extracted editable version of %(spec)s to %(dirname)s + + If it uses setuptools in its setup script, you can activate it in + "development" mode by going to that directory and running:: + + %(python)s setup.py develop + + See the setuptools documentation for the "develop" command for more info. + """).lstrip() + + def report_editable(self, spec, setup_script): + dirname = os.path.dirname(setup_script) + python = sys.executable + return '\n' + self.__editable_msg % locals() + + def run_setup(self, setup_script, setup_base, args): + sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg) + sys.modules.setdefault('distutils.command.egg_info', egg_info) + + args = list(args) + if self.verbose > 2: + v = 'v' * (self.verbose - 1) + args.insert(0, '-' + v) + elif self.verbose < 2: + args.insert(0, '-q') + if self.dry_run: + args.insert(0, '-n') + log.info( + "Running %s %s", setup_script[len(setup_base) + 1:], ' '.join(args) + ) + try: + run_setup(setup_script, args) + except SystemExit as v: + raise DistutilsError("Setup script exited with %s" % (v.args[0],)) + + def build_and_install(self, setup_script, setup_base): + args = ['bdist_egg', '--dist-dir'] + + dist_dir = tempfile.mkdtemp( + prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script) + ) + try: + self._set_fetcher_options(os.path.dirname(setup_script)) + args.append(dist_dir) + + self.run_setup(setup_script, setup_base, args) + all_eggs = Environment([dist_dir]) + eggs = [] + for key in all_eggs: + for dist in all_eggs[key]: + eggs.append(self.install_egg(dist.location, setup_base)) + if not eggs and not self.dry_run: + log.warn("No eggs found in %s (setup script problem?)", + dist_dir) + return eggs + finally: + rmtree(dist_dir) + log.set_verbosity(self.verbose) # restore our log verbosity + + def _set_fetcher_options(self, base): + """ + When easy_install is about to run bdist_egg on a source dist, that + source dist might have 'setup_requires' directives, requiring + additional fetching. Ensure the fetcher options given to easy_install + are available to that command as well. + """ + # find the fetch options from easy_install and write them out + # to the setup.cfg file. + ei_opts = self.distribution.get_option_dict('easy_install').copy() + fetch_directives = ( + 'find_links', 'site_dirs', 'index_url', 'optimize', + 'site_dirs', 'allow_hosts', + ) + fetch_options = {} + for key, val in ei_opts.items(): + if key not in fetch_directives: + continue + fetch_options[key.replace('_', '-')] = val[1] + # create a settings dictionary suitable for `edit_config` + settings = dict(easy_install=fetch_options) + cfg_filename = os.path.join(base, 'setup.cfg') + setopt.edit_config(cfg_filename, settings) + + def update_pth(self, dist): + if self.pth_file is None: + return + + for d in self.pth_file[dist.key]: # drop old entries + if self.multi_version or d.location != dist.location: + log.info("Removing %s from easy-install.pth file", d) + self.pth_file.remove(d) + if d.location in self.shadow_path: + self.shadow_path.remove(d.location) + + if not self.multi_version: + if dist.location in self.pth_file.paths: + log.info( + "%s is already the active version in easy-install.pth", + dist + ) + else: + log.info("Adding %s to easy-install.pth file", dist) + self.pth_file.add(dist) # add new entry + if dist.location not in self.shadow_path: + self.shadow_path.append(dist.location) + + if not self.dry_run: + + self.pth_file.save() + + if dist.key == 'setuptools': + # Ensure that setuptools itself never becomes unavailable! + # XXX should this check for latest version? + filename = os.path.join(self.install_dir, 'setuptools.pth') + if os.path.islink(filename): + os.unlink(filename) + f = open(filename, 'wt') + f.write(self.pth_file.make_relative(dist.location) + '\n') + f.close() + + def unpack_progress(self, src, dst): + # Progress filter for unpacking + log.debug("Unpacking %s to %s", src, dst) + return dst # only unpack-and-compile skips files for dry run + + def unpack_and_compile(self, egg_path, destination): + to_compile = [] + to_chmod = [] + + def pf(src, dst): + if dst.endswith('.py') and not src.startswith('EGG-INFO/'): + to_compile.append(dst) + elif dst.endswith('.dll') or dst.endswith('.so'): + to_chmod.append(dst) + self.unpack_progress(src, dst) + return not self.dry_run and dst or None + + unpack_archive(egg_path, destination, pf) + self.byte_compile(to_compile) + if not self.dry_run: + for f in to_chmod: + mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755 + chmod(f, mode) + + def byte_compile(self, to_compile): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + + try: + # try to make the byte compile messages quieter + log.set_verbosity(self.verbose - 1) + + byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run) + if self.optimize: + byte_compile( + to_compile, optimize=self.optimize, force=1, + dry_run=self.dry_run + ) + finally: + log.set_verbosity(self.verbose) # restore original verbosity + + __no_default_msg = textwrap.dedent(""" + bad install directory or PYTHONPATH + + You are attempting to install a package to a directory that is not + on PYTHONPATH and which Python does not read ".pth" files from. The + installation directory you specified (via --install-dir, --prefix, or + the distutils default setting) was: + + %s + + and your PYTHONPATH environment variable currently contains: + + %r + + Here are some of your options for correcting the problem: + + * You can choose a different installation directory, i.e., one that is + on PYTHONPATH or supports .pth files + + * You can add the installation directory to the PYTHONPATH environment + variable. (It must then also be on PYTHONPATH whenever you run + Python and want to use the package(s) you are installing.) + + * You can set up the installation directory to support ".pth" files by + using one of the approaches described here: + + https://pythonhosted.org/setuptools/easy_install.html#custom-installation-locations + + Please make the appropriate changes for your system and try again.""").lstrip() + + def no_default_version_msg(self): + template = self.__no_default_msg + return template % (self.install_dir, os.environ.get('PYTHONPATH', '')) + + def install_site_py(self): + """Make sure there's a site.py in the target dir, if needed""" + + if self.sitepy_installed: + return # already did it, or don't need to + + sitepy = os.path.join(self.install_dir, "site.py") + source = resource_string("setuptools", "site-patch.py") + source = source.decode('utf-8') + current = "" + + if os.path.exists(sitepy): + log.debug("Checking existing site.py in %s", self.install_dir) + with io.open(sitepy) as strm: + current = strm.read() + + if not current.startswith('def __boot():'): + raise DistutilsError( + "%s is not a setuptools-generated site.py; please" + " remove it." % sitepy + ) + + if current != source: + log.info("Creating %s", sitepy) + if not self.dry_run: + ensure_directory(sitepy) + with io.open(sitepy, 'w', encoding='utf-8') as strm: + strm.write(source) + self.byte_compile([sitepy]) + + self.sitepy_installed = True + + def create_home_path(self): + """Create directories under ~.""" + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in six.iteritems(self.config_vars): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0o700)" % path) + os.makedirs(path, 0o700) + + if sys.version[:3] in ('2.3', '2.4', '2.5') or 'real_prefix' in sys.__dict__: + sitedir_name = 'site-packages' + else: + sitedir_name = 'dist-packages' + + INSTALL_SCHEMES = dict( + posix=dict( + install_dir='$base/lib/python$py_version_short/site-packages', + script_dir='$base/bin', + ), + unix_local = dict( + install_dir = '$base/local/lib/python$py_version_short/%s' % sitedir_name, + script_dir = '$base/local/bin', + ), + posix_local = dict( + install_dir = '$base/local/lib/python$py_version_short/%s' % sitedir_name, + script_dir = '$base/local/bin', + ), + deb_system = dict( + install_dir = '$base/lib/python3/%s' % sitedir_name, + script_dir = '$base/bin', + ), + ) + + DEFAULT_SCHEME = dict( + install_dir='$base/Lib/site-packages', + script_dir='$base/Scripts', + ) + + def _expand(self, *attrs): + config_vars = self.get_finalized_command('install').config_vars + + if self.prefix or self.install_layout: + if self.install_layout and self.install_layout in ['deb']: + scheme_name = "deb_system" + self.prefix = '/usr' + elif self.prefix or 'real_prefix' in sys.__dict__: + scheme_name = os.name + else: + scheme_name = "posix_local" + # Set default install_dir/scripts from --prefix + config_vars = config_vars.copy() + config_vars['base'] = self.prefix + scheme = self.INSTALL_SCHEMES.get(scheme_name,self.DEFAULT_SCHEME) + for attr, val in scheme.items(): + if getattr(self, attr, None) is None: + setattr(self, attr, val) + + from distutils.util import subst_vars + + for attr in attrs: + val = getattr(self, attr) + if val is not None: + val = subst_vars(val, config_vars) + if os.name == 'posix': + val = os.path.expanduser(val) + setattr(self, attr, val) + + +def get_site_dirs(): + # return a list of 'site' dirs + sitedirs = [_f for _f in os.environ.get('PYTHONPATH', + '').split(os.pathsep) if _f] + prefixes = [sys.prefix] + if sys.exec_prefix != sys.prefix: + prefixes.append(sys.exec_prefix) + for prefix in prefixes: + if prefix: + if sys.platform in ('os2emx', 'riscos'): + sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) + elif os.sep == '/': + sitedirs.extend([os.path.join(prefix, + "lib", + "python" + sys.version[:3], + "site-packages"), + os.path.join(prefix, "lib", "site-python")]) + else: + if sys.version[:3] in ('2.3', '2.4', '2.5'): + sdir = "site-packages" + else: + sdir = "dist-packages" + sitedirs.extend( + [os.path.join(prefix, "local/lib", "python" + sys.version[:3], sdir), + os.path.join(prefix, "lib", "python" + sys.version[:3], sdir)] + ) + if sys.platform == 'darwin': + # for framework builds *only* we add the standard Apple + # locations. Currently only per-user, but /Library and + # /Network/Library could be added too + if 'Python.framework' in prefix: + home = os.environ.get('HOME') + if home: + sitedirs.append( + os.path.join(home, + 'Library', + 'Python', + sys.version[:3], + 'site-packages')) + lib_paths = get_path('purelib'), get_path('platlib') + for site_lib in lib_paths: + if site_lib not in sitedirs: + sitedirs.append(site_lib) + + if site.ENABLE_USER_SITE: + sitedirs.append(site.USER_SITE) + + sitedirs = list(map(normalize_path, sitedirs)) + + return sitedirs + + +def expand_paths(inputs): + """Yield sys.path directories that might contain "old-style" packages""" + + seen = {} + + for dirname in inputs: + dirname = normalize_path(dirname) + if dirname in seen: + continue + + seen[dirname] = 1 + if not os.path.isdir(dirname): + continue + + files = os.listdir(dirname) + yield dirname, files + + for name in files: + if not name.endswith('.pth'): + # We only care about the .pth files + continue + if name in ('easy-install.pth', 'setuptools.pth'): + # Ignore .pth files that we control + continue + + # Read the .pth file + f = open(os.path.join(dirname, name)) + lines = list(yield_lines(f)) + f.close() + + # Yield existing non-dupe, non-import directory lines from it + for line in lines: + if not line.startswith("import"): + line = normalize_path(line.rstrip()) + if line not in seen: + seen[line] = 1 + if not os.path.isdir(line): + continue + yield line, os.listdir(line) + + +def extract_wininst_cfg(dist_filename): + """Extract configuration data from a bdist_wininst .exe + + Returns a configparser.RawConfigParser, or None + """ + f = open(dist_filename, 'rb') + try: + endrec = zipfile._EndRecData(f) + if endrec is None: + return None + + prepended = (endrec[9] - endrec[5]) - endrec[6] + if prepended < 12: # no wininst data here + return None + f.seek(prepended - 12) + + tag, cfglen, bmlen = struct.unpack("<iii", f.read(12)) + if tag not in (0x1234567A, 0x1234567B): + return None # not a valid tag + + f.seek(prepended - (12 + cfglen)) + cfg = configparser.RawConfigParser( + {'version': '', 'target_version': ''}) + try: + part = f.read(cfglen) + # Read up to the first null byte. + config = part.split(b'\0', 1)[0] + # Now the config is in bytes, but for RawConfigParser, it should + # be text, so decode it. + config = config.decode(sys.getfilesystemencoding()) + cfg.readfp(six.StringIO(config)) + except configparser.Error: + return None + if not cfg.has_section('metadata') or not cfg.has_section('Setup'): + return None + return cfg + + finally: + f.close() + + +def get_exe_prefixes(exe_filename): + """Get exe->egg path translations for a given .exe file""" + + prefixes = [ + ('PURELIB/', ''), ('PLATLIB/pywin32_system32', ''), + ('PLATLIB/', ''), + ('SCRIPTS/', 'EGG-INFO/scripts/'), + ('DATA/lib/site-packages', ''), + ] + z = zipfile.ZipFile(exe_filename) + try: + for info in z.infolist(): + name = info.filename + parts = name.split('/') + if len(parts) == 3 and parts[2] == 'PKG-INFO': + if parts[1].endswith('.egg-info'): + prefixes.insert(0, ('/'.join(parts[:2]), 'EGG-INFO/')) + break + if len(parts) != 2 or not name.endswith('.pth'): + continue + if name.endswith('-nspkg.pth'): + continue + if parts[0].upper() in ('PURELIB', 'PLATLIB'): + contents = z.read(name) + if six.PY3: + contents = contents.decode() + for pth in yield_lines(contents): + pth = pth.strip().replace('\\', '/') + if not pth.startswith('import'): + prefixes.append((('%s/%s/' % (parts[0], pth)), '')) + finally: + z.close() + prefixes = [(x.lower(), y) for x, y in prefixes] + prefixes.sort() + prefixes.reverse() + return prefixes + + +def parse_requirement_arg(spec): + try: + return Requirement.parse(spec) + except ValueError: + raise DistutilsError( + "Not a URL, existing file, or requirement spec: %r" % (spec,) + ) + + +class PthDistributions(Environment): + """A .pth file with Distribution paths in it""" + + dirty = False + + def __init__(self, filename, sitedirs=()): + self.filename = filename + self.sitedirs = list(map(normalize_path, sitedirs)) + self.basedir = normalize_path(os.path.dirname(self.filename)) + self._load() + Environment.__init__(self, [], None, None) + for path in yield_lines(self.paths): + list(map(self.add, find_distributions(path, True))) + + def _load(self): + self.paths = [] + saw_import = False + seen = dict.fromkeys(self.sitedirs) + if os.path.isfile(self.filename): + f = open(self.filename, 'rt') + for line in f: + if line.startswith('import'): + saw_import = True + continue + path = line.rstrip() + self.paths.append(path) + if not path.strip() or path.strip().startswith('#'): + continue + # skip non-existent paths, in case somebody deleted a package + # manually, and duplicate paths as well + path = self.paths[-1] = normalize_path( + os.path.join(self.basedir, path) + ) + if not os.path.exists(path) or path in seen: + self.paths.pop() # skip it + self.dirty = True # we cleaned up, so we're dirty now :) + continue + seen[path] = 1 + f.close() + + if self.paths and not saw_import: + self.dirty = True # ensure anything we touch has import wrappers + while self.paths and not self.paths[-1].strip(): + self.paths.pop() + + def save(self): + """Write changed .pth file back to disk""" + if not self.dirty: + return + + rel_paths = list(map(self.make_relative, self.paths)) + if rel_paths: + log.debug("Saving %s", self.filename) + lines = self._wrap_lines(rel_paths) + data = '\n'.join(lines) + '\n' + + if os.path.islink(self.filename): + os.unlink(self.filename) + with open(self.filename, 'wt') as f: + f.write(data) + + elif os.path.exists(self.filename): + log.debug("Deleting empty %s", self.filename) + os.unlink(self.filename) + + self.dirty = False + + @staticmethod + def _wrap_lines(lines): + return lines + + def add(self, dist): + """Add `dist` to the distribution map""" + new_path = ( + dist.location not in self.paths and ( + dist.location not in self.sitedirs or + # account for '.' being in PYTHONPATH + dist.location == os.getcwd() + ) + ) + if new_path: + self.paths.append(dist.location) + self.dirty = True + Environment.add(self, dist) + + def remove(self, dist): + """Remove `dist` from the distribution map""" + while dist.location in self.paths: + self.paths.remove(dist.location) + self.dirty = True + Environment.remove(self, dist) + + def make_relative(self, path): + npath, last = os.path.split(normalize_path(path)) + baselen = len(self.basedir) + parts = [last] + sep = os.altsep == '/' and '/' or os.sep + while len(npath) >= baselen: + if npath == self.basedir: + parts.append(os.curdir) + parts.reverse() + return sep.join(parts) + npath, last = os.path.split(npath) + parts.append(last) + else: + return path + + +class RewritePthDistributions(PthDistributions): + + @classmethod + def _wrap_lines(cls, lines): + yield cls.prelude + for line in lines: + yield line + yield cls.postlude + + _inline = lambda text: textwrap.dedent(text).strip().replace('\n', '; ') + prelude = _inline(""" + import sys + sys.__plen = len(sys.path) + """) + postlude = _inline(""" + import sys + new = sys.path[sys.__plen:] + del sys.path[sys.__plen:] + p = getattr(sys, '__egginsert', 0) + sys.path[p:p] = new + sys.__egginsert = p + len(new) + """) + + +if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'rewrite') == 'rewrite': + PthDistributions = RewritePthDistributions + + +def _first_line_re(): + """ + Return a regular expression based on first_line_re suitable for matching + strings. + """ + if isinstance(first_line_re.pattern, str): + return first_line_re + + # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern. + return re.compile(first_line_re.pattern.decode()) + + +def auto_chmod(func, arg, exc): + if func is os.remove and os.name == 'nt': + chmod(arg, stat.S_IWRITE) + return func(arg) + et, ev, _ = sys.exc_info() + six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg)))) + + +def update_dist_caches(dist_path, fix_zipimporter_caches): + """ + Fix any globally cached `dist_path` related data + + `dist_path` should be a path of a newly installed egg distribution (zipped + or unzipped). + + sys.path_importer_cache contains finder objects that have been cached when + importing data from the original distribution. Any such finders need to be + cleared since the replacement distribution might be packaged differently, + e.g. a zipped egg distribution might get replaced with an unzipped egg + folder or vice versa. Having the old finders cached may then cause Python + to attempt loading modules from the replacement distribution using an + incorrect loader. + + zipimport.zipimporter objects are Python loaders charged with importing + data packaged inside zip archives. If stale loaders referencing the + original distribution, are left behind, they can fail to load modules from + the replacement distribution. E.g. if an old zipimport.zipimporter instance + is used to load data from a new zipped egg archive, it may cause the + operation to attempt to locate the requested data in the wrong location - + one indicated by the original distribution's zip archive directory + information. Such an operation may then fail outright, e.g. report having + read a 'bad local file header', or even worse, it may fail silently & + return invalid data. + + zipimport._zip_directory_cache contains cached zip archive directory + information for all existing zipimport.zipimporter instances and all such + instances connected to the same archive share the same cached directory + information. + + If asked, and the underlying Python implementation allows it, we can fix + all existing zipimport.zipimporter instances instead of having to track + them down and remove them one by one, by updating their shared cached zip + archive directory information. This, of course, assumes that the + replacement distribution is packaged as a zipped egg. + + If not asked to fix existing zipimport.zipimporter instances, we still do + our best to clear any remaining zipimport.zipimporter related cached data + that might somehow later get used when attempting to load data from the new + distribution and thus cause such load operations to fail. Note that when + tracking down such remaining stale data, we can not catch every conceivable + usage from here, and we clear only those that we know of and have found to + cause problems if left alive. Any remaining caches should be updated by + whomever is in charge of maintaining them, i.e. they should be ready to + handle us replacing their zip archives with new distributions at runtime. + + """ + # There are several other known sources of stale zipimport.zipimporter + # instances that we do not clear here, but might if ever given a reason to + # do so: + # * Global setuptools pkg_resources.working_set (a.k.a. 'master working + # set') may contain distributions which may in turn contain their + # zipimport.zipimporter loaders. + # * Several zipimport.zipimporter loaders held by local variables further + # up the function call stack when running the setuptools installation. + # * Already loaded modules may have their __loader__ attribute set to the + # exact loader instance used when importing them. Python 3.4 docs state + # that this information is intended mostly for introspection and so is + # not expected to cause us problems. + normalized_path = normalize_path(dist_path) + _uncache(normalized_path, sys.path_importer_cache) + if fix_zipimporter_caches: + _replace_zip_directory_cache_data(normalized_path) + else: + # Here, even though we do not want to fix existing and now stale + # zipimporter cache information, we still want to remove it. Related to + # Python's zip archive directory information cache, we clear each of + # its stale entries in two phases: + # 1. Clear the entry so attempting to access zip archive information + # via any existing stale zipimport.zipimporter instances fails. + # 2. Remove the entry from the cache so any newly constructed + # zipimport.zipimporter instances do not end up using old stale + # zip archive directory information. + # This whole stale data removal step does not seem strictly necessary, + # but has been left in because it was done before we started replacing + # the zip archive directory information cache content if possible, and + # there are no relevant unit tests that we can depend on to tell us if + # this is really needed. + _remove_and_clear_zip_directory_cache_data(normalized_path) + + +def _collect_zipimporter_cache_entries(normalized_path, cache): + """ + Return zipimporter cache entry keys related to a given normalized path. + + Alternative path spellings (e.g. those using different character case or + those using alternative path separators) related to the same path are + included. Any sub-path entries are included as well, i.e. those + corresponding to zip archives embedded in other zip archives. + + """ + result = [] + prefix_len = len(normalized_path) + for p in cache: + np = normalize_path(p) + if (np.startswith(normalized_path) and + np[prefix_len:prefix_len + 1] in (os.sep, '')): + result.append(p) + return result + + +def _update_zipimporter_cache(normalized_path, cache, updater=None): + """ + Update zipimporter cache data for a given normalized path. + + Any sub-path entries are processed as well, i.e. those corresponding to zip + archives embedded in other zip archives. + + Given updater is a callable taking a cache entry key and the original entry + (after already removing the entry from the cache), and expected to update + the entry and possibly return a new one to be inserted in its place. + Returning None indicates that the entry should not be replaced with a new + one. If no updater is given, the cache entries are simply removed without + any additional processing, the same as if the updater simply returned None. + + """ + for p in _collect_zipimporter_cache_entries(normalized_path, cache): + # N.B. pypy's custom zipimport._zip_directory_cache implementation does + # not support the complete dict interface: + # * Does not support item assignment, thus not allowing this function + # to be used only for removing existing cache entries. + # * Does not support the dict.pop() method, forcing us to use the + # get/del patterns instead. For more detailed information see the + # following links: + # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420 + # https://bitbucket.org/pypy/pypy/src/dd07756a34a41f674c0cacfbc8ae1d4cc9ea2ae4/pypy/module/zipimport/interp_zipimport.py#cl-99 + old_entry = cache[p] + del cache[p] + new_entry = updater and updater(p, old_entry) + if new_entry is not None: + cache[p] = new_entry + + +def _uncache(normalized_path, cache): + _update_zipimporter_cache(normalized_path, cache) + + +def _remove_and_clear_zip_directory_cache_data(normalized_path): + def clear_and_remove_cached_zip_archive_directory_data(path, old_entry): + old_entry.clear() + + _update_zipimporter_cache( + normalized_path, zipimport._zip_directory_cache, + updater=clear_and_remove_cached_zip_archive_directory_data) + +# PyPy Python implementation does not allow directly writing to the +# zipimport._zip_directory_cache and so prevents us from attempting to correct +# its content. The best we can do there is clear the problematic cache content +# and have PyPy repopulate it as needed. The downside is that if there are any +# stale zipimport.zipimporter instances laying around, attempting to use them +# will fail due to not having its zip archive directory information available +# instead of being automatically corrected to use the new correct zip archive +# directory information. +if '__pypy__' in sys.builtin_module_names: + _replace_zip_directory_cache_data = \ + _remove_and_clear_zip_directory_cache_data +else: + def _replace_zip_directory_cache_data(normalized_path): + def replace_cached_zip_archive_directory_data(path, old_entry): + # N.B. In theory, we could load the zip directory information just + # once for all updated path spellings, and then copy it locally and + # update its contained path strings to contain the correct + # spelling, but that seems like a way too invasive move (this cache + # structure is not officially documented anywhere and could in + # theory change with new Python releases) for no significant + # benefit. + old_entry.clear() + zipimport.zipimporter(path) + old_entry.update(zipimport._zip_directory_cache[path]) + return old_entry + + _update_zipimporter_cache( + normalized_path, zipimport._zip_directory_cache, + updater=replace_cached_zip_archive_directory_data) + + +def is_python(text, filename='<string>'): + "Is this string a valid Python script?" + try: + compile(text, filename, 'exec') + except (SyntaxError, TypeError): + return False + else: + return True + + +def is_sh(executable): + """Determine if the specified executable is a .sh (contains a #! line)""" + try: + with io.open(executable, encoding='latin-1') as fp: + magic = fp.read(2) + except (OSError, IOError): + return executable + return magic == '#!' + + +def nt_quote_arg(arg): + """Quote a command line argument according to Windows parsing rules""" + return subprocess.list2cmdline([arg]) + + +def is_python_script(script_text, filename): + """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc. + """ + if filename.endswith('.py') or filename.endswith('.pyw'): + return True # extension says it's Python + if is_python(script_text, filename): + return True # it's syntactically valid Python + if script_text.startswith('#!'): + # It begins with a '#!' line, so check if 'python' is in it somewhere + return 'python' in script_text.splitlines()[0].lower() + + return False # Not any Python I can recognize + + +try: + from os import chmod as _chmod +except ImportError: + # Jython compatibility + def _chmod(*args): + pass + + +def chmod(path, mode): + log.debug("changing mode of %s to %o", path, mode) + try: + _chmod(path, mode) + except os.error as e: + log.debug("chmod failed: %s", e) + + +class CommandSpec(list): + """ + A command spec for a #! header, specified as a list of arguments akin to + those passed to Popen. + """ + + options = [] + split_args = dict() + + @classmethod + def best(cls): + """ + Choose the best CommandSpec class based on environmental conditions. + """ + return cls + + @classmethod + def _sys_executable(cls): + _default = os.path.normpath(sys.executable) + return os.environ.get('__PYVENV_LAUNCHER__', _default) + + @classmethod + def from_param(cls, param): + """ + Construct a CommandSpec from a parameter to build_scripts, which may + be None. + """ + if isinstance(param, cls): + return param + if isinstance(param, list): + return cls(param) + if param is None: + return cls.from_environment() + # otherwise, assume it's a string. + return cls.from_string(param) + + @classmethod + def from_environment(cls): + return cls([cls._sys_executable()]) + + @classmethod + def from_string(cls, string): + """ + Construct a command spec from a simple string representing a command + line parseable by shlex.split. + """ + items = shlex.split(string, **cls.split_args) + return cls(items) + + def install_options(self, script_text): + self.options = shlex.split(self._extract_options(script_text)) + cmdline = subprocess.list2cmdline(self) + if not isascii(cmdline): + self.options[:0] = ['-x'] + + @staticmethod + def _extract_options(orig_script): + """ + Extract any options from the first line of the script. + """ + first = (orig_script + '\n').splitlines()[0] + match = _first_line_re().match(first) + options = match.group(1) or '' if match else '' + return options.strip() + + def as_header(self): + return self._render(self + list(self.options)) + + @staticmethod + def _render(items): + cmdline = subprocess.list2cmdline(items) + return '#!' + cmdline + '\n' + +# For pbr compat; will be removed in a future version. +sys_executable = CommandSpec._sys_executable() + + +class WindowsCommandSpec(CommandSpec): + split_args = dict(posix=False) + + +class ScriptWriter(object): + """ + Encapsulates behavior around writing entry point scripts for console and + gui apps. + """ + + template = textwrap.dedent(""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + __requires__ = %(spec)r + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.exit( + load_entry_point(%(spec)r, %(group)r, %(name)r)() + ) + """).lstrip() + + command_spec_class = CommandSpec + + @classmethod + def get_script_args(cls, dist, executable=None, wininst=False): + # for backward compatibility + warnings.warn("Use get_args", DeprecationWarning) + writer = (WindowsScriptWriter if wininst else ScriptWriter).best() + header = cls.get_script_header("", executable, wininst) + return writer.get_args(dist, header) + + @classmethod + def get_script_header(cls, script_text, executable=None, wininst=False): + # for backward compatibility + warnings.warn("Use get_header", DeprecationWarning) + if wininst: + executable = "python.exe" + cmd = cls.command_spec_class.best().from_param(executable) + cmd.install_options(script_text) + return cmd.as_header() + + @classmethod + def get_args(cls, dist, header=None): + """ + Yield write_script() argument tuples for a distribution's + console_scripts and gui_scripts entry points. + """ + if header is None: + header = cls.get_header() + spec = str(dist.as_requirement()) + for type_ in 'console', 'gui': + group = type_ + '_scripts' + for name, ep in dist.get_entry_map(group).items(): + cls._ensure_safe_name(name) + script_text = cls.template % locals() + args = cls._get_script_args(type_, name, header, script_text) + for res in args: + yield res + + @staticmethod + def _ensure_safe_name(name): + """ + Prevent paths in *_scripts entry point names. + """ + has_path_sep = re.search(r'[\\/]', name) + if has_path_sep: + raise ValueError("Path separators not allowed in script names") + + @classmethod + def get_writer(cls, force_windows): + # for backward compatibility + warnings.warn("Use best", DeprecationWarning) + return WindowsScriptWriter.best() if force_windows else cls.best() + + @classmethod + def best(cls): + """ + Select the best ScriptWriter for this environment. + """ + if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'): + return WindowsScriptWriter.best() + else: + return cls + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + # Simply write the stub with no extension. + yield (name, header + script_text) + + @classmethod + def get_header(cls, script_text="", executable=None): + """Create a #! line, getting options (if any) from script_text""" + cmd = cls.command_spec_class.best().from_param(executable) + cmd.install_options(script_text) + return cmd.as_header() + + +class WindowsScriptWriter(ScriptWriter): + command_spec_class = WindowsCommandSpec + + @classmethod + def get_writer(cls): + # for backward compatibility + warnings.warn("Use best", DeprecationWarning) + return cls.best() + + @classmethod + def best(cls): + """ + Select the best ScriptWriter suitable for Windows + """ + writer_lookup = dict( + executable=WindowsExecutableLauncherWriter, + natural=cls, + ) + # for compatibility, use the executable launcher by default + launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable') + return writer_lookup[launcher] + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + "For Windows, add a .py extension" + ext = dict(console='.pya', gui='.pyw')[type_] + if ext not in os.environ['PATHEXT'].lower().split(';'): + warnings.warn("%s not listed in PATHEXT; scripts will not be " + "recognized as executables." % ext, UserWarning) + old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] + old.remove(ext) + header = cls._adjust_header(type_, header) + blockers = [name + x for x in old] + yield name + ext, header + script_text, 't', blockers + + @classmethod + def _adjust_header(cls, type_, orig_header): + """ + Make sure 'pythonw' is used for gui and and 'python' is used for + console (regardless of what sys.executable is). + """ + pattern = 'pythonw.exe' + repl = 'python.exe' + if type_ == 'gui': + pattern, repl = repl, pattern + pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE) + new_header = pattern_ob.sub(string=orig_header, repl=repl) + return new_header if cls._use_header(new_header) else orig_header + + @staticmethod + def _use_header(new_header): + """ + Should _adjust_header use the replaced header? + + On non-windows systems, always use. On + Windows systems, only use the replaced header if it resolves + to an executable on the system. + """ + clean_header = new_header[2:-1].strip('"') + return sys.platform != 'win32' or find_executable(clean_header) + + +class WindowsExecutableLauncherWriter(WindowsScriptWriter): + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + """ + For Windows, add a .py extension and an .exe launcher + """ + if type_ == 'gui': + launcher_type = 'gui' + ext = '-script.pyw' + old = ['.pyw'] + else: + launcher_type = 'cli' + ext = '-script.py' + old = ['.py', '.pyc', '.pyo'] + hdr = cls._adjust_header(type_, header) + blockers = [name + x for x in old] + yield (name + ext, hdr + script_text, 't', blockers) + yield ( + name + '.exe', get_win_launcher(launcher_type), + 'b' # write in binary mode + ) + if not is_64bit(): + # install a manifest for the launcher to prevent Windows + # from detecting it as an installer (which it will for + # launchers like easy_install.exe). Consider only + # adding a manifest for launchers detected as installers. + # See Distribute #143 for details. + m_name = name + '.exe.manifest' + yield (m_name, load_launcher_manifest(name), 't') + + +# for backward-compatibility +get_script_args = ScriptWriter.get_script_args +get_script_header = ScriptWriter.get_script_header + + +def get_win_launcher(type): + """ + Load the Windows launcher (executable) suitable for launching a script. + + `type` should be either 'cli' or 'gui' + + Returns the executable as a byte string. + """ + launcher_fn = '%s.exe' % type + if platform.machine().lower() == 'arm': + launcher_fn = launcher_fn.replace(".", "-arm.") + if is_64bit(): + launcher_fn = launcher_fn.replace(".", "-64.") + else: + launcher_fn = launcher_fn.replace(".", "-32.") + return resource_string('setuptools', launcher_fn) + + +def load_launcher_manifest(name): + manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml') + if six.PY2: + return manifest % vars() + else: + return manifest.decode('utf-8') % vars() + + +def rmtree(path, ignore_errors=False, onerror=auto_chmod): + """Recursively delete a directory tree. + + This code is taken from the Python 2.4 version of 'shutil', because + the 2.3 version doesn't really work right. + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + names = [] + try: + names = os.listdir(path) + except os.error: + onerror(os.listdir, path, sys.exc_info()) + for name in names: + fullname = os.path.join(path, name) + try: + mode = os.lstat(fullname).st_mode + except os.error: + mode = 0 + if stat.S_ISDIR(mode): + rmtree(fullname, ignore_errors, onerror) + else: + try: + os.remove(fullname) + except os.error: + onerror(os.remove, fullname, sys.exc_info()) + try: + os.rmdir(path) + except os.error: + onerror(os.rmdir, path, sys.exc_info()) + + +def current_umask(): + tmp = os.umask(0o022) + os.umask(tmp) + return tmp + + +def bootstrap(): + # This function is called when setuptools*.egg is run using /bin/sh + import setuptools + + argv0 = os.path.dirname(setuptools.__path__[0]) + sys.argv[0] = argv0 + sys.argv.append(argv0) + main() + + +def main(argv=None, **kw): + from setuptools import setup + from setuptools.dist import Distribution + + class DistributionWithoutHelpCommands(Distribution): + common_usage = "" + + def _show_help(self, *args, **kw): + with _patch_usage(): + Distribution._show_help(self, *args, **kw) + + if argv is None: + argv = sys.argv[1:] + + with _patch_usage(): + setup( + script_args=['-q', 'easy_install', '-v'] + argv, + script_name=sys.argv[0] or 'easy_install', + distclass=DistributionWithoutHelpCommands, **kw + ) + + +@contextlib.contextmanager +def _patch_usage(): + import distutils.core + USAGE = textwrap.dedent(""" + usage: %(script)s [options] requirement_or_url ... + or: %(script)s --help + """).lstrip() + + def gen_usage(script_name): + return USAGE % dict( + script=os.path.basename(script_name), + ) + + saved = distutils.core.gen_usage + distutils.core.gen_usage = gen_usage + try: + yield + finally: + distutils.core.gen_usage = saved diff --git a/venv/lib/python3.5/site-packages/setuptools/command/egg_info.py b/venv/lib/python3.5/site-packages/setuptools/command/egg_info.py new file mode 100644 index 0000000..d1bd9b0 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/egg_info.py @@ -0,0 +1,482 @@ +"""setuptools.command.egg_info + +Create a distribution's .egg-info directory and contents""" + +from distutils.filelist import FileList as _FileList +from distutils.util import convert_path +from distutils import log +import distutils.errors +import distutils.filelist +import os +import re +import sys +import io +import warnings +import time + +from setuptools.extern import six +from setuptools.extern.six.moves import map + +from setuptools import Command +from setuptools.command.sdist import sdist +from setuptools.command.sdist import walk_revctrl +from setuptools.command.setopt import edit_config +from setuptools.command import bdist_egg +from pkg_resources import ( + parse_requirements, safe_name, parse_version, + safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename) +import setuptools.unicode_utils as unicode_utils + +from pkg_resources.extern import packaging + +try: + from setuptools_svn import svn_utils +except ImportError: + pass + + +class egg_info(Command): + description = "create a distribution's .egg-info directory" + + user_options = [ + ('egg-base=', 'e', "directory containing .egg-info directories" + " (default: top of the source tree)"), + ('tag-svn-revision', 'r', + "Add subversion revision ID to version number"), + ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), + ('tag-build=', 'b', "Specify explicit tag to add to version number"), + ('no-svn-revision', 'R', + "Don't add subversion revision ID [default]"), + ('no-date', 'D', "Don't include date stamp [default]"), + ] + + boolean_options = ['tag-date', 'tag-svn-revision'] + negative_opt = {'no-svn-revision': 'tag-svn-revision', + 'no-date': 'tag-date'} + + def initialize_options(self): + self.egg_name = None + self.egg_version = None + self.egg_base = None + self.egg_info = None + self.tag_build = None + self.tag_svn_revision = 0 + self.tag_date = 0 + self.broken_egg_info = False + self.vtags = None + + def save_version_info(self, filename): + values = dict( + egg_info=dict( + tag_svn_revision=0, + tag_date=0, + tag_build=self.tags(), + ) + ) + edit_config(filename, values) + + def finalize_options(self): + self.egg_name = safe_name(self.distribution.get_name()) + self.vtags = self.tags() + self.egg_version = self.tagged_version() + + parsed_version = parse_version(self.egg_version) + + try: + is_version = isinstance(parsed_version, packaging.version.Version) + spec = ( + "%s==%s" if is_version else "%s===%s" + ) + list( + parse_requirements(spec % (self.egg_name, self.egg_version)) + ) + except ValueError: + raise distutils.errors.DistutilsOptionError( + "Invalid distribution name or version syntax: %s-%s" % + (self.egg_name, self.egg_version) + ) + + if self.egg_base is None: + dirs = self.distribution.package_dir + self.egg_base = (dirs or {}).get('', os.curdir) + + self.ensure_dirname('egg_base') + self.egg_info = to_filename(self.egg_name) + '.egg-info' + if self.egg_base != os.curdir: + self.egg_info = os.path.join(self.egg_base, self.egg_info) + if '-' in self.egg_name: + self.check_broken_egg_info() + + # Set package version for the benefit of dumber commands + # (e.g. sdist, bdist_wininst, etc.) + # + self.distribution.metadata.version = self.egg_version + + # If we bootstrapped around the lack of a PKG-INFO, as might be the + # case in a fresh checkout, make sure that any special tags get added + # to the version info + # + pd = self.distribution._patched_dist + if pd is not None and pd.key == self.egg_name.lower(): + pd._version = self.egg_version + pd._parsed_version = parse_version(self.egg_version) + self.distribution._patched_dist = None + + def write_or_delete_file(self, what, filename, data, force=False): + """Write `data` to `filename` or delete if empty + + If `data` is non-empty, this routine is the same as ``write_file()``. + If `data` is empty but not ``None``, this is the same as calling + ``delete_file(filename)`. If `data` is ``None``, then this is a no-op + unless `filename` exists, in which case a warning is issued about the + orphaned file (if `force` is false), or deleted (if `force` is true). + """ + if data: + self.write_file(what, filename, data) + elif os.path.exists(filename): + if data is None and not force: + log.warn( + "%s not set in setup(), but %s exists", what, filename + ) + return + else: + self.delete_file(filename) + + def write_file(self, what, filename, data): + """Write `data` to `filename` (if not a dry run) after announcing it + + `what` is used in a log message to identify what is being written + to the file. + """ + log.info("writing %s to %s", what, filename) + if six.PY3: + data = data.encode("utf-8") + if not self.dry_run: + f = open(filename, 'wb') + f.write(data) + f.close() + + def delete_file(self, filename): + """Delete `filename` (if not a dry run) after announcing it""" + log.info("deleting %s", filename) + if not self.dry_run: + os.unlink(filename) + + def tagged_version(self): + version = self.distribution.get_version() + # egg_info may be called more than once for a distribution, + # in which case the version string already contains all tags. + if self.vtags and version.endswith(self.vtags): + return safe_version(version) + return safe_version(version + self.vtags) + + def run(self): + self.mkpath(self.egg_info) + installer = self.distribution.fetch_build_egg + for ep in iter_entry_points('egg_info.writers'): + ep.require(installer=installer) + writer = ep.resolve() + writer(self, ep.name, os.path.join(self.egg_info, ep.name)) + + # Get rid of native_libs.txt if it was put there by older bdist_egg + nl = os.path.join(self.egg_info, "native_libs.txt") + if os.path.exists(nl): + self.delete_file(nl) + + self.find_sources() + + def tags(self): + version = '' + if self.tag_build: + version += self.tag_build + if self.tag_svn_revision: + version += '-r%s' % self.get_svn_revision() + if self.tag_date: + version += time.strftime("-%Y%m%d") + return version + + @staticmethod + def get_svn_revision(): + if 'svn_utils' not in globals(): + return "0" + return str(svn_utils.SvnInfo.load(os.curdir).get_revision()) + + def find_sources(self): + """Generate SOURCES.txt manifest file""" + manifest_filename = os.path.join(self.egg_info, "SOURCES.txt") + mm = manifest_maker(self.distribution) + mm.manifest = manifest_filename + mm.run() + self.filelist = mm.filelist + + def check_broken_egg_info(self): + bei = self.egg_name + '.egg-info' + if self.egg_base != os.curdir: + bei = os.path.join(self.egg_base, bei) + if os.path.exists(bei): + log.warn( + "-" * 78 + '\n' + "Note: Your current .egg-info directory has a '-' in its name;" + '\nthis will not work correctly with "setup.py develop".\n\n' + 'Please rename %s to %s to correct this problem.\n' + '-' * 78, + bei, self.egg_info + ) + self.broken_egg_info = self.egg_info + self.egg_info = bei # make it work for now + + +class FileList(_FileList): + """File list that accepts only existing, platform-independent paths""" + + def append(self, item): + if item.endswith('\r'): # Fix older sdists built on Windows + item = item[:-1] + path = convert_path(item) + + if self._safe_path(path): + self.files.append(path) + + def extend(self, paths): + self.files.extend(filter(self._safe_path, paths)) + + def _repair(self): + """ + Replace self.files with only safe paths + + Because some owners of FileList manipulate the underlying + ``files`` attribute directly, this method must be called to + repair those paths. + """ + self.files = list(filter(self._safe_path, self.files)) + + def _safe_path(self, path): + enc_warn = "'%s' not %s encodable -- skipping" + + # To avoid accidental trans-codings errors, first to unicode + u_path = unicode_utils.filesys_decode(path) + if u_path is None: + log.warn("'%s' in unexpected encoding -- skipping" % path) + return False + + # Must ensure utf-8 encodability + utf8_path = unicode_utils.try_encode(u_path, "utf-8") + if utf8_path is None: + log.warn(enc_warn, path, 'utf-8') + return False + + try: + # accept is either way checks out + if os.path.exists(u_path) or os.path.exists(utf8_path): + return True + # this will catch any encode errors decoding u_path + except UnicodeEncodeError: + log.warn(enc_warn, path, sys.getfilesystemencoding()) + + +class manifest_maker(sdist): + template = "MANIFEST.in" + + def initialize_options(self): + self.use_defaults = 1 + self.prune = 1 + self.manifest_only = 1 + self.force_manifest = 1 + + def finalize_options(self): + pass + + def run(self): + self.filelist = FileList() + if not os.path.exists(self.manifest): + self.write_manifest() # it must exist so it'll get in the list + self.filelist.findall() + self.add_defaults() + if os.path.exists(self.template): + self.read_template() + self.prune_file_list() + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def _manifest_normalize(self, path): + path = unicode_utils.filesys_decode(path) + return path.replace(os.sep, '/') + + def write_manifest(self): + """ + Write the file list in 'self.filelist' to the manifest file + named by 'self.manifest'. + """ + self.filelist._repair() + + # Now _repairs should encodability, but not unicode + files = [self._manifest_normalize(f) for f in self.filelist.files] + msg = "writing manifest file '%s'" % self.manifest + self.execute(write_file, (self.manifest, files), msg) + + def warn(self, msg): # suppress missing-file warnings from sdist + if not msg.startswith("standard file not found:"): + sdist.warn(self, msg) + + def add_defaults(self): + sdist.add_defaults(self) + self.filelist.append(self.template) + self.filelist.append(self.manifest) + rcfiles = list(walk_revctrl()) + if rcfiles: + self.filelist.extend(rcfiles) + elif os.path.exists(self.manifest): + self.read_manifest() + ei_cmd = self.get_finalized_command('egg_info') + self._add_egg_info(cmd=ei_cmd) + self.filelist.include_pattern("*", prefix=ei_cmd.egg_info) + + def _add_egg_info(self, cmd): + """ + Add paths for egg-info files for an external egg-base. + + The egg-info files are written to egg-base. If egg-base is + outside the current working directory, this method + searchs the egg-base directory for files to include + in the manifest. Uses distutils.filelist.findall (which is + really the version monkeypatched in by setuptools/__init__.py) + to perform the search. + + Since findall records relative paths, prefix the returned + paths with cmd.egg_base, so add_default's include_pattern call + (which is looking for the absolute cmd.egg_info) will match + them. + """ + if cmd.egg_base == os.curdir: + # egg-info files were already added by something else + return + + discovered = distutils.filelist.findall(cmd.egg_base) + resolved = (os.path.join(cmd.egg_base, path) for path in discovered) + self.filelist.allfiles.extend(resolved) + + def prune_file_list(self): + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + sep = re.escape(os.sep) + self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep, + is_regex=1) + + +def write_file(filename, contents): + """Create a file with the specified name and write 'contents' (a + sequence of strings without line terminators) to it. + """ + contents = "\n".join(contents) + + # assuming the contents has been vetted for utf-8 encoding + contents = contents.encode("utf-8") + + with open(filename, "wb") as f: # always write POSIX-style manifest + f.write(contents) + + +def write_pkg_info(cmd, basename, filename): + log.info("writing %s", filename) + if not cmd.dry_run: + metadata = cmd.distribution.metadata + metadata.version, oldver = cmd.egg_version, metadata.version + metadata.name, oldname = cmd.egg_name, metadata.name + try: + # write unescaped data to PKG-INFO, so older pkg_resources + # can still parse it + metadata.write_pkg_info(cmd.egg_info) + finally: + metadata.name, metadata.version = oldname, oldver + + safe = getattr(cmd.distribution, 'zip_safe', None) + + bdist_egg.write_safety_flag(cmd.egg_info, safe) + + +def warn_depends_obsolete(cmd, basename, filename): + if os.path.exists(filename): + log.warn( + "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." + ) + + +def _write_requirements(stream, reqs): + lines = yield_lines(reqs or ()) + append_cr = lambda line: line + '\n' + lines = map(append_cr, lines) + stream.writelines(lines) + + +def write_requirements(cmd, basename, filename): + dist = cmd.distribution + data = six.StringIO() + _write_requirements(data, dist.install_requires) + extras_require = dist.extras_require or {} + for extra in sorted(extras_require): + data.write('\n[{extra}]\n'.format(**vars())) + _write_requirements(data, extras_require[extra]) + cmd.write_or_delete_file("requirements", filename, data.getvalue()) + + +def write_setup_requirements(cmd, basename, filename): + data = StringIO() + _write_requirements(data, cmd.distribution.setup_requires) + cmd.write_or_delete_file("setup-requirements", filename, data.getvalue()) + + +def write_toplevel_names(cmd, basename, filename): + pkgs = dict.fromkeys( + [ + k.split('.', 1)[0] + for k in cmd.distribution.iter_distribution_names() + ] + ) + cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n') + + +def overwrite_arg(cmd, basename, filename): + write_arg(cmd, basename, filename, True) + + +def write_arg(cmd, basename, filename, force=False): + argname = os.path.splitext(basename)[0] + value = getattr(cmd.distribution, argname, None) + if value is not None: + value = '\n'.join(value) + '\n' + cmd.write_or_delete_file(argname, filename, value, force) + + +def write_entries(cmd, basename, filename): + ep = cmd.distribution.entry_points + + if isinstance(ep, six.string_types) or ep is None: + data = ep + elif ep is not None: + data = [] + for section, contents in sorted(ep.items()): + if not isinstance(contents, six.string_types): + contents = EntryPoint.parse_group(section, contents) + contents = '\n'.join(sorted(map(str, contents.values()))) + data.append('[%s]\n%s\n\n' % (section, contents)) + data = ''.join(data) + + cmd.write_or_delete_file('entry points', filename, data, True) + + +def get_pkg_info_revision(): + """ + Get a -r### off of PKG-INFO Version in case this is an sdist of + a subversion revision. + """ + warnings.warn("get_pkg_info_revision is deprecated.", DeprecationWarning) + if os.path.exists('PKG-INFO'): + with io.open('PKG-INFO') as f: + for line in f: + match = re.match(r"Version:.*-r(\d+)\s*$", line) + if match: + return int(match.group(1)) + return 0 diff --git a/venv/lib/python3.5/site-packages/setuptools/command/install.py b/venv/lib/python3.5/site-packages/setuptools/command/install.py new file mode 100644 index 0000000..31a5ddb --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/install.py @@ -0,0 +1,125 @@ +from distutils.errors import DistutilsArgError +import inspect +import glob +import warnings +import platform +import distutils.command.install as orig + +import setuptools + +# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for +# now. See https://github.com/pypa/setuptools/issues/199/ +_install = orig.install + + +class install(orig.install): + """Use easy_install to install the package, w/dependencies""" + + user_options = orig.install.user_options + [ + ('old-and-unmanageable', None, "Try not to use this!"), + ('single-version-externally-managed', None, + "used by system package builders to create 'flat' eggs"), + ] + boolean_options = orig.install.boolean_options + [ + 'old-and-unmanageable', 'single-version-externally-managed', + ] + new_commands = [ + ('install_egg_info', lambda self: True), + ('install_scripts', lambda self: True), + ] + _nc = dict(new_commands) + + def initialize_options(self): + orig.install.initialize_options(self) + self.old_and_unmanageable = None + self.single_version_externally_managed = None + + def finalize_options(self): + orig.install.finalize_options(self) + if self.root: + self.single_version_externally_managed = True + elif self.single_version_externally_managed: + if not self.root and not self.record: + raise DistutilsArgError( + "You must specify --record or --root when building system" + " packages" + ) + + def handle_extra_path(self): + if self.root or self.single_version_externally_managed: + # explicit backward-compatibility mode, allow extra_path to work + return orig.install.handle_extra_path(self) + + # Ignore extra_path when installing an egg (or being run by another + # command without --root or --single-version-externally-managed + self.path_file = None + self.extra_dirs = '' + + def run(self): + # Explicit request for old-style install? Just do it + if self.old_and_unmanageable or self.single_version_externally_managed: + return orig.install.run(self) + + if not self._called_from_setup(inspect.currentframe()): + # Run in backward-compatibility mode to support bdist_* commands. + orig.install.run(self) + else: + self.do_egg_install() + + @staticmethod + def _called_from_setup(run_frame): + """ + Attempt to detect whether run() was called from setup() or by another + command. If called by setup(), the parent caller will be the + 'run_command' method in 'distutils.dist', and *its* caller will be + the 'run_commands' method. If called any other way, the + immediate caller *might* be 'run_command', but it won't have been + called by 'run_commands'. Return True in that case or if a call stack + is unavailable. Return False otherwise. + """ + if run_frame is None: + msg = "Call stack not available. bdist_* commands may fail." + warnings.warn(msg) + if platform.python_implementation() == 'IronPython': + msg = "For best results, pass -X:Frames to enable call stack." + warnings.warn(msg) + return True + res = inspect.getouterframes(run_frame)[2] + caller, = res[:1] + info = inspect.getframeinfo(caller) + caller_module = caller.f_globals.get('__name__', '') + return ( + caller_module == 'distutils.dist' + and info.function == 'run_commands' + ) + + def do_egg_install(self): + + easy_install = self.distribution.get_command_class('easy_install') + + cmd = easy_install( + self.distribution, args="x", root=self.root, record=self.record, + ) + cmd.ensure_finalized() # finalize before bdist_egg munges install cmd + cmd.always_copy_from = '.' # make sure local-dir eggs get installed + + # pick up setup-dir .egg files only: no .egg-info + cmd.package_index.scan(glob.glob('*.egg')) + + self.run_command('bdist_egg') + args = [self.distribution.get_command_obj('bdist_egg').egg_output] + + if setuptools.bootstrap_install_from: + # Bootstrap self-installation of setuptools + args.insert(0, setuptools.bootstrap_install_from) + + cmd.args = args + cmd.run() + setuptools.bootstrap_install_from = None + + +# XXX Python 3.1 doesn't see _nc if this is inside the class +install.sub_commands = ( + [cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc] + + install.new_commands +) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/install_egg_info.py b/venv/lib/python3.5/site-packages/setuptools/command/install_egg_info.py new file mode 100644 index 0000000..ae0325d --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/install_egg_info.py @@ -0,0 +1,138 @@ +from distutils import log, dir_util +import os, sys + +from setuptools.extern.six.moves import map + +from setuptools import Command +from setuptools.archive_util import unpack_archive +import pkg_resources + + +class install_egg_info(Command): + """Install an .egg-info directory for the package""" + + description = "Install an .egg-info directory for the package" + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + self.install_layout = None + self.prefix_option = None + + def finalize_options(self): + self.set_undefined_options('install_lib', + ('install_dir', 'install_dir')) + self.set_undefined_options('install',('install_layout','install_layout')) + if sys.hexversion > 0x2060000: + self.set_undefined_options('install',('prefix_option','prefix_option')) + ei_cmd = self.get_finalized_command("egg_info") + basename = pkg_resources.Distribution( + None, None, ei_cmd.egg_name, ei_cmd.egg_version + ).egg_name() + '.egg-info' + + if self.install_layout: + if not self.install_layout.lower() in ['deb']: + raise DistutilsOptionError("unknown value for --install-layout") + self.install_layout = self.install_layout.lower() + basename = basename.replace('-py%s' % pkg_resources.PY_MAJOR, '') + elif self.prefix_option or 'real_prefix' in sys.__dict__: + # don't modify for virtualenv + pass + else: + basename = basename.replace('-py%s' % pkg_resources.PY_MAJOR, '') + + self.source = ei_cmd.egg_info + self.target = os.path.join(self.install_dir, basename) + self.outputs = [] + + def run(self): + self.run_command('egg_info') + if os.path.isdir(self.target) and not os.path.islink(self.target): + dir_util.remove_tree(self.target, dry_run=self.dry_run) + elif os.path.exists(self.target): + self.execute(os.unlink, (self.target,), "Removing " + self.target) + if not self.dry_run: + pkg_resources.ensure_directory(self.target) + self.execute( + self.copytree, (), "Copying %s to %s" % (self.source, self.target) + ) + self.install_namespaces() + + def get_outputs(self): + return self.outputs + + def copytree(self): + # Copy the .egg-info tree to site-packages + def skimmer(src, dst): + # filter out source-control directories; note that 'src' is always + # a '/'-separated path, regardless of platform. 'dst' is a + # platform-specific path. + for skip in '.svn/', 'CVS/': + if src.startswith(skip) or '/' + skip in src: + return None + if self.install_layout and self.install_layout in ['deb'] and src.startswith('SOURCES.txt'): + log.info("Skipping SOURCES.txt") + return None + self.outputs.append(dst) + log.debug("Copying %s to %s", src, dst) + return dst + + unpack_archive(self.source, self.target, skimmer) + + def install_namespaces(self): + nsp = self._get_all_ns_packages() + if not nsp: + return + filename, ext = os.path.splitext(self.target) + filename += '-nspkg.pth' + self.outputs.append(filename) + log.info("Installing %s", filename) + lines = map(self._gen_nspkg_line, nsp) + + if self.dry_run: + # always generate the lines, even in dry run + list(lines) + return + + with open(filename, 'wt') as f: + f.writelines(lines) + + _nspkg_tmpl = ( + "import sys, types, os", + "p = os.path.join(sys._getframe(1).f_locals['sitedir'], *%(pth)r)", + "ie = os.path.exists(os.path.join(p,'__init__.py'))", + "m = not ie and " + "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", + "mp = (m or []) and m.__dict__.setdefault('__path__',[])", + "(p not in mp) and mp.append(p)", + ) + "lines for the namespace installer" + + _nspkg_tmpl_multi = ( + 'm and setattr(sys.modules[%(parent)r], %(child)r, m)', + ) + "additional line(s) when a parent package is indicated" + + @classmethod + def _gen_nspkg_line(cls, pkg): + # ensure pkg is not a unicode string under Python 2.7 + pkg = str(pkg) + pth = tuple(pkg.split('.')) + tmpl_lines = cls._nspkg_tmpl + parent, sep, child = pkg.rpartition('.') + if parent: + tmpl_lines += cls._nspkg_tmpl_multi + return ';'.join(tmpl_lines) % locals() + '\n' + + def _get_all_ns_packages(self): + """Return sorted list of all package namespaces""" + nsp = set() + for pkg in self.distribution.namespace_packages or []: + pkg = pkg.split('.') + while pkg: + nsp.add('.'.join(pkg)) + pkg.pop() + return sorted(nsp) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/install_lib.py b/venv/lib/python3.5/site-packages/setuptools/command/install_lib.py new file mode 100644 index 0000000..696b776 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/install_lib.py @@ -0,0 +1,147 @@ +import os +import sys +import imp +from itertools import product, starmap +import distutils.command.install_lib as orig + +class install_lib(orig.install_lib): + """Don't add compiled flags to filenames of non-Python files""" + + def initialize_options(self): + orig.install_lib.initialize_options(self) + self.multiarch = None + self.install_layout = None + + def finalize_options(self): + orig.install_lib.finalize_options(self) + self.set_undefined_options('install',('install_layout','install_layout')) + if self.install_layout == 'deb' and sys.version_info[:2] >= (3, 3): + import sysconfig + self.multiarch = sysconfig.get_config_var('MULTIARCH') + + def run(self): + self.build() + outfiles = self.install() + if outfiles is not None: + # always compile, in case we have any extension stubs to deal with + self.byte_compile(outfiles) + + def get_exclusions(self): + """ + Return a collections.Sized collections.Container of paths to be + excluded for single_version_externally_managed installations. + """ + all_packages = ( + pkg + for ns_pkg in self._get_SVEM_NSPs() + for pkg in self._all_packages(ns_pkg) + ) + + excl_specs = product(all_packages, self._gen_exclusion_paths()) + return set(starmap(self._exclude_pkg_path, excl_specs)) + + def _exclude_pkg_path(self, pkg, exclusion_path): + """ + Given a package name and exclusion path within that package, + compute the full exclusion path. + """ + parts = pkg.split('.') + [exclusion_path] + return os.path.join(self.install_dir, *parts) + + @staticmethod + def _all_packages(pkg_name): + """ + >>> list(install_lib._all_packages('foo.bar.baz')) + ['foo.bar.baz', 'foo.bar', 'foo'] + """ + while pkg_name: + yield pkg_name + pkg_name, sep, child = pkg_name.rpartition('.') + + def _get_SVEM_NSPs(self): + """ + Get namespace packages (list) but only for + single_version_externally_managed installations and empty otherwise. + """ + # TODO: is it necessary to short-circuit here? i.e. what's the cost + # if get_finalized_command is called even when namespace_packages is + # False? + if not self.distribution.namespace_packages: + return [] + + install_cmd = self.get_finalized_command('install') + svem = install_cmd.single_version_externally_managed + + return self.distribution.namespace_packages if svem else [] + + @staticmethod + def _gen_exclusion_paths(): + """ + Generate file paths to be excluded for namespace packages (bytecode + cache files). + """ + # always exclude the package module itself + yield '__init__.py' + + yield '__init__.pyc' + yield '__init__.pyo' + + if not hasattr(imp, 'get_tag'): + return + + base = os.path.join('__pycache__', '__init__.' + imp.get_tag()) + yield base + '.pyc' + yield base + '.pyo' + yield base + '.opt-1.pyc' + yield base + '.opt-2.pyc' + + def copy_tree( + self, infile, outfile, + preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1 + ): + assert preserve_mode and preserve_times and not preserve_symlinks + exclude = self.get_exclusions() + + if not exclude: + import distutils.dir_util + distutils.dir_util._multiarch = self.multiarch + return orig.install_lib.copy_tree(self, infile, outfile) + + # Exclude namespace package __init__.py* files from the output + + from setuptools.archive_util import unpack_directory + from distutils import log + + outfiles = [] + + if self.multiarch: + import sysconfig + ext_suffix = sysconfig.get_config_var ('EXT_SUFFIX') + if ext_suffix.endswith(self.multiarch + ext_suffix[-3:]): + new_suffix = None + else: + new_suffix = "%s-%s%s" % (ext_suffix[:-3], self.multiarch, ext_suffix[-3:]) + + def pf(src, dst): + if dst in exclude: + log.warn("Skipping installation of %s (namespace package)", + dst) + return False + + if self.multiarch and new_suffix and dst.endswith(ext_suffix) and not dst.endswith(new_suffix): + dst = dst.replace(ext_suffix, new_suffix) + log.info("renaming extension to %s", os.path.basename(dst)) + + log.info("copying %s -> %s", src, os.path.dirname(dst)) + outfiles.append(dst) + return dst + + unpack_directory(infile, outfile, pf) + return outfiles + + def get_outputs(self): + outputs = orig.install_lib.get_outputs(self) + exclude = self.get_exclusions() + if exclude: + return [f for f in outputs if f not in exclude] + return outputs diff --git a/venv/lib/python3.5/site-packages/setuptools/command/install_scripts.py b/venv/lib/python3.5/site-packages/setuptools/command/install_scripts.py new file mode 100644 index 0000000..be66cb2 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/install_scripts.py @@ -0,0 +1,60 @@ +from distutils import log +import distutils.command.install_scripts as orig +import os + +from pkg_resources import Distribution, PathMetadata, ensure_directory + + +class install_scripts(orig.install_scripts): + """Do normal script install, plus any egg_info wrapper scripts""" + + def initialize_options(self): + orig.install_scripts.initialize_options(self) + self.no_ep = False + + def run(self): + import setuptools.command.easy_install as ei + + self.run_command("egg_info") + if self.distribution.scripts: + orig.install_scripts.run(self) # run first to set up self.outfiles + else: + self.outfiles = [] + if self.no_ep: + # don't install entry point scripts into .egg file! + return + + ei_cmd = self.get_finalized_command("egg_info") + dist = Distribution( + ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info), + ei_cmd.egg_name, ei_cmd.egg_version, + ) + bs_cmd = self.get_finalized_command('build_scripts') + exec_param = getattr(bs_cmd, 'executable', None) + bw_cmd = self.get_finalized_command("bdist_wininst") + is_wininst = getattr(bw_cmd, '_is_running', False) + writer = ei.ScriptWriter + if is_wininst: + exec_param = "python.exe" + writer = ei.WindowsScriptWriter + # resolve the writer to the environment + writer = writer.best() + cmd = writer.command_spec_class.best().from_param(exec_param) + for args in writer.get_args(dist, cmd.as_header()): + self.write_script(*args) + + def write_script(self, script_name, contents, mode="t", *ignored): + """Write an executable file to the scripts directory""" + from setuptools.command.easy_install import chmod, current_umask + + log.info("Installing %s script to %s", script_name, self.install_dir) + target = os.path.join(self.install_dir, script_name) + self.outfiles.append(target) + + mask = current_umask() + if not self.dry_run: + ensure_directory(target) + f = open(target, "w" + mode) + f.write(contents) + f.close() + chmod(target, 0o777 - mask) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/register.py b/venv/lib/python3.5/site-packages/setuptools/command/register.py new file mode 100644 index 0000000..8d6336a --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/register.py @@ -0,0 +1,10 @@ +import distutils.command.register as orig + + +class register(orig.register): + __doc__ = orig.register.__doc__ + + def run(self): + # Make sure that we are using valid current name/version info + self.run_command('egg_info') + orig.register.run(self) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/rotate.py b/venv/lib/python3.5/site-packages/setuptools/command/rotate.py new file mode 100644 index 0000000..804f962 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/rotate.py @@ -0,0 +1,62 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import os + +from setuptools.extern import six + +from setuptools import Command + + +class rotate(Command): + """Delete older distributions""" + + description = "delete older distributions, keeping N newest files" + user_options = [ + ('match=', 'm', "patterns to match (required)"), + ('dist-dir=', 'd', "directory where the distributions are"), + ('keep=', 'k', "number of matching distributions to keep"), + ] + + boolean_options = [] + + def initialize_options(self): + self.match = None + self.dist_dir = None + self.keep = None + + def finalize_options(self): + if self.match is None: + raise DistutilsOptionError( + "Must specify one or more (comma-separated) match patterns " + "(e.g. '.zip' or '.egg')" + ) + if self.keep is None: + raise DistutilsOptionError("Must specify number of files to keep") + try: + self.keep = int(self.keep) + except ValueError: + raise DistutilsOptionError("--keep must be an integer") + if isinstance(self.match, six.string_types): + self.match = [ + convert_path(p.strip()) for p in self.match.split(',') + ] + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + + def run(self): + self.run_command("egg_info") + from glob import glob + + for pattern in self.match: + pattern = self.distribution.get_name() + '*' + pattern + files = glob(os.path.join(self.dist_dir, pattern)) + files = [(os.path.getmtime(f), f) for f in files] + files.sort() + files.reverse() + + log.info("%d file(s) matching %s", len(files), pattern) + files = files[self.keep:] + for (t, f) in files: + log.info("Deleting %s", f) + if not self.dry_run: + os.unlink(f) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/saveopts.py b/venv/lib/python3.5/site-packages/setuptools/command/saveopts.py new file mode 100644 index 0000000..611cec5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/saveopts.py @@ -0,0 +1,22 @@ +from setuptools.command.setopt import edit_config, option_base + + +class saveopts(option_base): + """Save command-line options to a file""" + + description = "save supplied options to setup.cfg or other config file" + + def run(self): + dist = self.distribution + settings = {} + + for cmd in dist.command_options: + + if cmd == 'saveopts': + continue # don't save our own options! + + for opt, (src, val) in dist.get_option_dict(cmd).items(): + if src == "command line": + settings.setdefault(cmd, {})[opt] = val + + edit_config(self.filename, settings, self.dry_run) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/sdist.py b/venv/lib/python3.5/site-packages/setuptools/command/sdist.py new file mode 100644 index 0000000..6640d4e --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/sdist.py @@ -0,0 +1,196 @@ +from glob import glob +from distutils import log +import distutils.command.sdist as orig +import os +import sys +import io + +from setuptools.extern import six + +from setuptools.utils import cs_path_exists + +import pkg_resources + +READMES = 'README', 'README.rst', 'README.txt' + +_default_revctrl = list + +def walk_revctrl(dirname=''): + """Find all files under revision control""" + for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): + for item in ep.load()(dirname): + yield item + + +class sdist(orig.sdist): + """Smart sdist that finds anything supported by revision control""" + + user_options = [ + ('formats=', None, + "formats for source distribution (comma-separated list)"), + ('keep-temp', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), + ] + + negative_opt = {} + + def run(self): + self.run_command('egg_info') + ei_cmd = self.get_finalized_command('egg_info') + self.filelist = ei_cmd.filelist + self.filelist.append(os.path.join(ei_cmd.egg_info, 'SOURCES.txt')) + self.check_readme() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # Call check_metadata only if no 'check' command + # (distutils <= 2.6) + import distutils.command + + if 'check' not in distutils.command.__all__: + self.check_metadata() + + self.make_distribution() + + dist_files = getattr(self.distribution, 'dist_files', []) + for file in self.archive_files: + data = ('sdist', '', file) + if data not in dist_files: + dist_files.append(data) + + def __read_template_hack(self): + # This grody hack closes the template file (MANIFEST.in) if an + # exception occurs during read_template. + # Doing so prevents an error when easy_install attempts to delete the + # file. + try: + orig.sdist.read_template(self) + except: + _, _, tb = sys.exc_info() + tb.tb_next.tb_frame.f_locals['template'].close() + raise + + # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle + # has been fixed, so only override the method if we're using an earlier + # Python. + has_leaky_handle = ( + sys.version_info < (2, 7, 2) + or (3, 0) <= sys.version_info < (3, 1, 4) + or (3, 2) <= sys.version_info < (3, 2, 1) + ) + if has_leaky_handle: + read_template = __read_template_hack + + def add_defaults(self): + standards = [READMES, + self.distribution.script_name] + for fn in standards: + if isinstance(fn, tuple): + alts = fn + got_it = 0 + for fn in alts: + if cs_path_exists(fn): + got_it = 1 + self.filelist.append(fn) + break + + if not got_it: + self.warn("standard file not found: should have one of " + + ', '.join(alts)) + else: + if cs_path_exists(fn): + self.filelist.append(fn) + else: + self.warn("standard file '%s' not found" % fn) + + optional = ['test/test*.py', 'setup.cfg'] + for pattern in optional: + files = list(filter(cs_path_exists, glob(pattern))) + if files: + self.filelist.extend(files) + + # getting python files + if self.distribution.has_pure_modules(): + build_py = self.get_finalized_command('build_py') + self.filelist.extend(build_py.get_source_files()) + # This functionality is incompatible with include_package_data, and + # will in fact create an infinite recursion if include_package_data + # is True. Use of include_package_data will imply that + # distutils-style automatic handling of package_data is disabled + if not self.distribution.include_package_data: + for _, src_dir, _, filenames in build_py.data_files: + self.filelist.extend([os.path.join(src_dir, filename) + for filename in filenames]) + + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) + + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) + + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + + def check_readme(self): + for f in READMES: + if os.path.exists(f): + return + else: + self.warn( + "standard file not found: should have one of " + + ', '.join(READMES) + ) + + def make_release_tree(self, base_dir, files): + orig.sdist.make_release_tree(self, base_dir, files) + + # Save any egg_info command line options used to create this sdist + dest = os.path.join(base_dir, 'setup.cfg') + if hasattr(os, 'link') and os.path.exists(dest): + # unlink and re-copy, since it might be hard-linked, and + # we don't want to change the source version + os.unlink(dest) + self.copy_file('setup.cfg', dest) + + self.get_finalized_command('egg_info').save_version_info(dest) + + def _manifest_is_not_generated(self): + # check for special comment used in 2.7.1 and higher + if not os.path.isfile(self.manifest): + return False + + with io.open(self.manifest, 'rb') as fp: + first_line = fp.readline() + return (first_line != + '# file GENERATED by distutils, do NOT edit\n'.encode()) + + def read_manifest(self): + """Read the manifest file (named by 'self.manifest') and use it to + fill in 'self.filelist', the list of files to include in the source + distribution. + """ + log.info("reading manifest file '%s'", self.manifest) + manifest = open(self.manifest, 'rbU') + for line in manifest: + # The manifest must contain UTF-8. See #303. + if six.PY3: + try: + line = line.decode('UTF-8') + except UnicodeDecodeError: + log.warn("%r not UTF-8 decodable -- skipping" % line) + continue + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) + manifest.close() diff --git a/venv/lib/python3.5/site-packages/setuptools/command/setopt.py b/venv/lib/python3.5/site-packages/setuptools/command/setopt.py new file mode 100644 index 0000000..7f332be --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/setopt.py @@ -0,0 +1,150 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import distutils +import os + +from setuptools.extern.six.moves import configparser + +from setuptools import Command + + +__all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] + + +def config_file(kind="local"): + """Get the filename of the distutils, local, global, or per-user config + + `kind` must be one of "local", "global", or "user" + """ + if kind == 'local': + return 'setup.cfg' + if kind == 'global': + return os.path.join( + os.path.dirname(distutils.__file__), 'distutils.cfg' + ) + if kind == 'user': + dot = os.name == 'posix' and '.' or '' + return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot)) + raise ValueError( + "config_file() type must be 'local', 'global', or 'user'", kind + ) + + +def edit_config(filename, settings, dry_run=False): + """Edit a configuration file to include `settings` + + `settings` is a dictionary of dictionaries or ``None`` values, keyed by + command/section name. A ``None`` value means to delete the entire section, + while a dictionary lists settings to be changed or deleted in that section. + A setting of ``None`` means to delete that setting. + """ + log.debug("Reading configuration from %s", filename) + opts = configparser.RawConfigParser() + opts.read([filename]) + for section, options in settings.items(): + if options is None: + log.info("Deleting section [%s] from %s", section, filename) + opts.remove_section(section) + else: + if not opts.has_section(section): + log.debug("Adding new section [%s] to %s", section, filename) + opts.add_section(section) + for option, value in options.items(): + if value is None: + log.debug( + "Deleting %s.%s from %s", + section, option, filename + ) + opts.remove_option(section, option) + if not opts.options(section): + log.info("Deleting empty [%s] section from %s", + section, filename) + opts.remove_section(section) + else: + log.debug( + "Setting %s.%s to %r in %s", + section, option, value, filename + ) + opts.set(section, option, value) + + log.info("Writing %s", filename) + if not dry_run: + with open(filename, 'w') as f: + opts.write(f) + + +class option_base(Command): + """Abstract base class for commands that mess with config files""" + + user_options = [ + ('global-config', 'g', + "save options to the site-wide distutils.cfg file"), + ('user-config', 'u', + "save options to the current user's pydistutils.cfg file"), + ('filename=', 'f', + "configuration file to use (default=setup.cfg)"), + ] + + boolean_options = [ + 'global-config', 'user-config', + ] + + def initialize_options(self): + self.global_config = None + self.user_config = None + self.filename = None + + def finalize_options(self): + filenames = [] + if self.global_config: + filenames.append(config_file('global')) + if self.user_config: + filenames.append(config_file('user')) + if self.filename is not None: + filenames.append(self.filename) + if not filenames: + filenames.append(config_file('local')) + if len(filenames) > 1: + raise DistutilsOptionError( + "Must specify only one configuration file option", + filenames + ) + self.filename, = filenames + + +class setopt(option_base): + """Save command-line options to a file""" + + description = "set an option in setup.cfg or another config file" + + user_options = [ + ('command=', 'c', 'command to set an option for'), + ('option=', 'o', 'option to set'), + ('set-value=', 's', 'value of the option'), + ('remove', 'r', 'remove (unset) the value'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.command = None + self.option = None + self.set_value = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.command is None or self.option is None: + raise DistutilsOptionError("Must specify --command *and* --option") + if self.set_value is None and not self.remove: + raise DistutilsOptionError("Must specify --set-value or --remove") + + def run(self): + edit_config( + self.filename, { + self.command: {self.option.replace('-', '_'): self.set_value} + }, + self.dry_run + ) diff --git a/venv/lib/python3.5/site-packages/setuptools/command/test.py b/venv/lib/python3.5/site-packages/setuptools/command/test.py new file mode 100644 index 0000000..371e913 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/test.py @@ -0,0 +1,196 @@ +from distutils.errors import DistutilsOptionError +from unittest import TestLoader +import sys + +from setuptools.extern import six +from setuptools.extern.six.moves import map + +from pkg_resources import (resource_listdir, resource_exists, normalize_path, + working_set, _namespace_packages, + add_activation_listener, require, EntryPoint) +from setuptools import Command +from setuptools.py31compat import unittest_main + + +class ScanningLoader(TestLoader): + def loadTestsFromModule(self, module, pattern=None): + """Return a suite of all tests cases contained in the given module + + If the module is a package, load tests from all the modules in it. + If the module has an ``additional_tests`` function, call it and add + the return value to the tests. + """ + tests = [] + tests.append(TestLoader.loadTestsFromModule(self, module)) + + if hasattr(module, "additional_tests"): + tests.append(module.additional_tests()) + + if hasattr(module, '__path__'): + for file in resource_listdir(module.__name__, ''): + if file.endswith('.py') and file != '__init__.py': + submodule = module.__name__ + '.' + file[:-3] + else: + if resource_exists(module.__name__, file + '/__init__.py'): + submodule = module.__name__ + '.' + file + else: + continue + tests.append(self.loadTestsFromName(submodule)) + + if len(tests) != 1: + return self.suiteClass(tests) + else: + return tests[0] # don't create a nested suite for only one return + + +# adapted from jaraco.classes.properties:NonDataProperty +class NonDataProperty(object): + def __init__(self, fget): + self.fget = fget + + def __get__(self, obj, objtype=None): + if obj is None: + return self + return self.fget(obj) + + +class test(Command): + """Command to run unit tests after in-place build""" + + description = "run unit tests after in-place build" + + user_options = [ + ('test-module=', 'm', "Run 'test_suite' in specified module"), + ('test-suite=', 's', + "Test suite to run (e.g. 'some_module.test_suite')"), + ('test-runner=', 'r', "Test runner to use"), + ] + + def initialize_options(self): + self.test_suite = None + self.test_module = None + self.test_loader = None + self.test_runner = None + + def finalize_options(self): + + if self.test_suite and self.test_module: + msg = "You may specify a module or a suite, but not both" + raise DistutilsOptionError(msg) + + if self.test_suite is None: + if self.test_module is None: + self.test_suite = self.distribution.test_suite + else: + self.test_suite = self.test_module + ".test_suite" + + if self.test_loader is None: + self.test_loader = getattr(self.distribution, 'test_loader', None) + if self.test_loader is None: + self.test_loader = "setuptools.command.test:ScanningLoader" + if self.test_runner is None: + self.test_runner = getattr(self.distribution, 'test_runner', None) + + @NonDataProperty + def test_args(self): + return list(self._test_args()) + + def _test_args(self): + if self.verbose: + yield '--verbose' + if self.test_suite: + yield self.test_suite + + def with_project_on_sys_path(self, func): + with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False) + + if with_2to3: + # If we run 2to3 we can not do this inplace: + + # Ensure metadata is up-to-date + self.reinitialize_command('build_py', inplace=0) + self.run_command('build_py') + bpy_cmd = self.get_finalized_command("build_py") + build_path = normalize_path(bpy_cmd.build_lib) + + # Build extensions + self.reinitialize_command('egg_info', egg_base=build_path) + self.run_command('egg_info') + + self.reinitialize_command('build_ext', inplace=0) + self.run_command('build_ext') + else: + # Without 2to3 inplace works fine: + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + ei_cmd = self.get_finalized_command("egg_info") + + old_path = sys.path[:] + old_modules = sys.modules.copy() + + try: + sys.path.insert(0, normalize_path(ei_cmd.egg_base)) + working_set.__init__() + add_activation_listener(lambda dist: dist.activate()) + require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) + func() + finally: + sys.path[:] = old_path + sys.modules.clear() + sys.modules.update(old_modules) + working_set.__init__() + + def run(self): + if self.distribution.install_requires: + self.distribution.fetch_build_eggs( + self.distribution.install_requires) + if self.distribution.tests_require: + self.distribution.fetch_build_eggs(self.distribution.tests_require) + + cmd = ' '.join(self._argv) + if self.dry_run: + self.announce('skipping "%s" (dry run)' % cmd) + else: + self.announce('running "%s"' % cmd) + self.with_project_on_sys_path(self.run_tests) + + def run_tests(self): + # Purge modules under test from sys.modules. The test loader will + # re-import them from the build location. Required when 2to3 is used + # with namespace packages. + if six.PY3 and getattr(self.distribution, 'use_2to3', False): + module = self.test_suite.split('.')[0] + if module in _namespace_packages: + del_modules = [] + if module in sys.modules: + del_modules.append(module) + module += '.' + for name in sys.modules: + if name.startswith(module): + del_modules.append(name) + list(map(sys.modules.__delitem__, del_modules)) + + unittest_main( + None, None, self._argv, + testLoader=self._resolve_as_ep(self.test_loader), + testRunner=self._resolve_as_ep(self.test_runner), + ) + + @property + def _argv(self): + return ['unittest'] + self.test_args + + @staticmethod + def _resolve_as_ep(val): + """ + Load the indicated attribute value, called, as a as if it were + specified as an entry point. + """ + if val is None: + return + parsed = EntryPoint.parse("x=" + val) + return parsed.resolve()() diff --git a/venv/lib/python3.5/site-packages/setuptools/command/upload.py b/venv/lib/python3.5/site-packages/setuptools/command/upload.py new file mode 100644 index 0000000..08c20ba --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/upload.py @@ -0,0 +1,23 @@ +from distutils.command import upload as orig + + +class upload(orig.upload): + """ + Override default upload behavior to look up password + in the keyring if available. + """ + + def finalize_options(self): + orig.upload.finalize_options(self) + self.password or self._load_password_from_keyring() + + def _load_password_from_keyring(self): + """ + Attempt to load password from keyring. Suppress Exceptions. + """ + try: + keyring = __import__('keyring') + self.password = keyring.get_password(self.repository, + self.username) + except Exception: + pass diff --git a/venv/lib/python3.5/site-packages/setuptools/command/upload_docs.py b/venv/lib/python3.5/site-packages/setuptools/command/upload_docs.py new file mode 100644 index 0000000..f887b47 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/command/upload_docs.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +"""upload_docs + +Implements a Distutils 'upload_docs' subcommand (upload documentation to +PyPI's pythonhosted.org). +""" + +from base64 import standard_b64encode +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import socket +import zipfile +import tempfile +import shutil + +from setuptools.extern import six +from setuptools.extern.six.moves import http_client, urllib + +from pkg_resources import iter_entry_points +from .upload import upload + + +errors = 'surrogateescape' if six.PY3 else 'strict' + + +# This is not just a replacement for byte literals +# but works as a general purpose encoder +def b(s, encoding='utf-8'): + if isinstance(s, six.text_type): + return s.encode(encoding, errors) + return s + + +class upload_docs(upload): + description = 'Upload documentation to PyPI' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), + ('upload-dir=', None, 'directory to upload'), + ] + boolean_options = upload.boolean_options + + def has_sphinx(self): + if self.upload_dir is None: + for ep in iter_entry_points('distutils.commands', 'build_sphinx'): + return True + + sub_commands = [('build_sphinx', has_sphinx)] + + def initialize_options(self): + upload.initialize_options(self) + self.upload_dir = None + self.target_dir = None + + def finalize_options(self): + upload.finalize_options(self) + if self.upload_dir is None: + if self.has_sphinx(): + build_sphinx = self.get_finalized_command('build_sphinx') + self.target_dir = build_sphinx.builder_target_dir + else: + build = self.get_finalized_command('build') + self.target_dir = os.path.join(build.build_base, 'docs') + else: + self.ensure_dirname('upload_dir') + self.target_dir = self.upload_dir + self.announce('Using upload directory %s' % self.target_dir) + + def create_zipfile(self, filename): + zip_file = zipfile.ZipFile(filename, "w") + try: + self.mkpath(self.target_dir) # just in case + for root, dirs, files in os.walk(self.target_dir): + if root == self.target_dir and not files: + raise DistutilsOptionError( + "no files found in upload directory '%s'" + % self.target_dir) + for name in files: + full = os.path.join(root, name) + relative = root[len(self.target_dir):].lstrip(os.path.sep) + dest = os.path.join(relative, name) + zip_file.write(full, dest) + finally: + zip_file.close() + + def run(self): + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + tmp_dir = tempfile.mkdtemp() + name = self.distribution.metadata.get_name() + zip_file = os.path.join(tmp_dir, "%s.zip" % name) + try: + self.create_zipfile(zip_file) + self.upload_file(zip_file) + finally: + shutil.rmtree(tmp_dir) + + def upload_file(self, filename): + f = open(filename, 'rb') + content = f.read() + f.close() + meta = self.distribution.metadata + data = { + ':action': 'doc_upload', + 'name': meta.get_name(), + 'content': (os.path.basename(filename), content), + } + # set up the authentication + credentials = b(self.username + ':' + self.password) + credentials = standard_b64encode(credentials) + if six.PY3: + credentials = credentials.decode('ascii') + auth = "Basic " + credentials + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b('\n--') + b(boundary) + end_boundary = sep_boundary + b('--') + body = [] + for key, values in six.iteritems(data): + title = '\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(values, list): + values = [values] + for value in values: + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = b(value) + body.append(sep_boundary) + body.append(b(title)) + body.append(b("\n\n")) + body.append(value) + if value and value[-1:] == b('\r'): + body.append(b('\n')) # write an extra newline (lurve Macs) + body.append(end_boundary) + body.append(b("\n")) + body = b('').join(body) + + self.announce("Submitting documentation to %s" % (self.repository), + log.INFO) + + # build the Request + # We can't use urllib2 since we need to send the Basic + # auth right with the first request + schema, netloc, url, params, query, fragments = \ + urllib.parse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + conn = http_client.HTTPConnection(netloc) + elif schema == 'https': + conn = http_client.HTTPSConnection(netloc) + else: + raise AssertionError("unsupported schema " + schema) + + data = '' + try: + conn.connect() + conn.putrequest("POST", url) + content_type = 'multipart/form-data; boundary=%s' % boundary + conn.putheader('Content-type', content_type) + conn.putheader('Content-length', str(len(body))) + conn.putheader('Authorization', auth) + conn.endheaders() + conn.send(body) + except socket.error as e: + self.announce(str(e), log.ERROR) + return + + r = conn.getresponse() + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), + log.INFO) + elif r.status == 301: + location = r.getheader('Location') + if location is None: + location = 'https://pythonhosted.org/%s/' % meta.get_name() + self.announce('Upload successful. Visit %s' % location, + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (r.status, r.reason), + log.ERROR) + if self.show_response: + print('-' * 75, r.read(), '-' * 75) diff --git a/venv/lib/python3.5/site-packages/setuptools/depends.py b/venv/lib/python3.5/site-packages/setuptools/depends.py new file mode 100644 index 0000000..9f7c9a3 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/depends.py @@ -0,0 +1,217 @@ +import sys +import imp +import marshal +from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN +from distutils.version import StrictVersion + +from setuptools.extern import six + +__all__ = [ + 'Require', 'find_module', 'get_module_constant', 'extract_constant' +] + +class Require: + """A prerequisite to building or installing a distribution""" + + def __init__(self, name, requested_version, module, homepage='', + attribute=None, format=None): + + if format is None and requested_version is not None: + format = StrictVersion + + if format is not None: + requested_version = format(requested_version) + if attribute is None: + attribute = '__version__' + + self.__dict__.update(locals()) + del self.self + + def full_name(self): + """Return full package/distribution name, w/version""" + if self.requested_version is not None: + return '%s-%s' % (self.name,self.requested_version) + return self.name + + def version_ok(self, version): + """Is 'version' sufficiently up-to-date?""" + return self.attribute is None or self.format is None or \ + str(version) != "unknown" and version >= self.requested_version + + def get_version(self, paths=None, default="unknown"): + + """Get version number of installed module, 'None', or 'default' + + Search 'paths' for module. If not found, return 'None'. If found, + return the extracted version attribute, or 'default' if no version + attribute was specified, or the value cannot be determined without + importing the module. The version is formatted according to the + requirement's version format (if any), unless it is 'None' or the + supplied 'default'. + """ + + if self.attribute is None: + try: + f,p,i = find_module(self.module,paths) + if f: f.close() + return default + except ImportError: + return None + + v = get_module_constant(self.module, self.attribute, default, paths) + + if v is not None and v is not default and self.format is not None: + return self.format(v) + + return v + + def is_present(self, paths=None): + """Return true if dependency is present on 'paths'""" + return self.get_version(paths) is not None + + def is_current(self, paths=None): + """Return true if dependency is present and up-to-date on 'paths'""" + version = self.get_version(paths) + if version is None: + return False + return self.version_ok(version) + + +def _iter_code(code): + + """Yield '(op,arg)' pair for each operation in code object 'code'""" + + from array import array + from dis import HAVE_ARGUMENT, EXTENDED_ARG + + bytes = array('b',code.co_code) + eof = len(code.co_code) + + ptr = 0 + extended_arg = 0 + + while ptr<eof: + + op = bytes[ptr] + + if op>=HAVE_ARGUMENT: + + arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg + ptr += 3 + + if op==EXTENDED_ARG: + long_type = six.integer_types[-1] + extended_arg = arg * long_type(65536) + continue + + else: + arg = None + ptr += 1 + + yield op,arg + + +def find_module(module, paths=None): + """Just like 'imp.find_module()', but with package support""" + + parts = module.split('.') + + while parts: + part = parts.pop(0) + f, path, (suffix,mode,kind) = info = imp.find_module(part, paths) + + if kind==PKG_DIRECTORY: + parts = parts or ['__init__'] + paths = [path] + + elif parts: + raise ImportError("Can't find %r in %s" % (parts,module)) + + return info + + +def get_module_constant(module, symbol, default=-1, paths=None): + + """Find 'module' by searching 'paths', and extract 'symbol' + + Return 'None' if 'module' does not exist on 'paths', or it does not define + 'symbol'. If the module defines 'symbol' as a constant, return the + constant. Otherwise, return 'default'.""" + + try: + f, path, (suffix, mode, kind) = find_module(module, paths) + except ImportError: + # Module doesn't exist + return None + + try: + if kind==PY_COMPILED: + f.read(8) # skip magic & date + code = marshal.load(f) + elif kind==PY_FROZEN: + code = imp.get_frozen_object(module) + elif kind==PY_SOURCE: + code = compile(f.read(), path, 'exec') + else: + # Not something we can parse; we'll have to import it. :( + if module not in sys.modules: + imp.load_module(module, f, path, (suffix, mode, kind)) + return getattr(sys.modules[module], symbol, None) + + finally: + if f: + f.close() + + return extract_constant(code, symbol, default) + + +def extract_constant(code, symbol, default=-1): + """Extract the constant value of 'symbol' from 'code' + + If the name 'symbol' is bound to a constant value by the Python code + object 'code', return that value. If 'symbol' is bound to an expression, + return 'default'. Otherwise, return 'None'. + + Return value is based on the first assignment to 'symbol'. 'symbol' must + be a global, or at least a non-"fast" local in the code block. That is, + only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' + must be present in 'code.co_names'. + """ + + if symbol not in code.co_names: + # name's not there, can't possibly be an assigment + return None + + name_idx = list(code.co_names).index(symbol) + + STORE_NAME = 90 + STORE_GLOBAL = 97 + LOAD_CONST = 100 + + const = default + + for op, arg in _iter_code(code): + + if op==LOAD_CONST: + const = code.co_consts[arg] + elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL): + return const + else: + const = default + + +def _update_globals(): + """ + Patch the globals to remove the objects not available on some platforms. + + XXX it'd be better to test assertions about bytecode instead. + """ + + if not sys.platform.startswith('java') and sys.platform != 'cli': + return + incompatible = 'extract_constant', 'get_module_constant' + for name in incompatible: + del globals()[name] + __all__.remove(name) + +_update_globals() diff --git a/venv/lib/python3.5/site-packages/setuptools/dist.py b/venv/lib/python3.5/site-packages/setuptools/dist.py new file mode 100644 index 0000000..086e0a5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/dist.py @@ -0,0 +1,872 @@ +__all__ = ['Distribution'] + +import re +import os +import sys +import warnings +import numbers +import distutils.log +import distutils.core +import distutils.cmd +import distutils.dist +from distutils.core import Distribution as _Distribution +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsSetupError) + +from setuptools.extern import six +from setuptools.extern.six.moves import map +from pkg_resources.extern import packaging + +from setuptools.depends import Require +from setuptools import windows_support +import pkg_resources + + +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls + +_Distribution = _get_unpatched(_Distribution) + +def _patch_distribution_metadata_write_pkg_info(): + """ + Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local + encoding to save the pkg_info. Monkey-patch its write_pkg_info method to + correct this undesirable behavior. + """ + environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2) + if not environment_local: + return + + # from Python 3.4 + def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree. + """ + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: + self.write_pkg_file(pkg_info) + + distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info +_patch_distribution_metadata_write_pkg_info() + +sequence = tuple, list + +def check_importable(dist, attr, value): + try: + ep = pkg_resources.EntryPoint.parse('x='+value) + assert not ep.extras + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be importable 'module:attrs' string (got %r)" + % (attr,value) + ) + + +def assert_string_list(dist, attr, value): + """Verify that value is a string list or None""" + try: + assert ''.join(value)!=value + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be a list of strings (got %r)" % (attr,value) + ) +def check_nsp(dist, attr, value): + """Verify that namespace packages are valid""" + assert_string_list(dist,attr,value) + for nsp in value: + if not dist.has_contents_for(nsp): + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + if '.' in nsp: + parent = '.'.join(nsp.split('.')[:-1]) + if parent not in value: + distutils.log.warn( + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", nsp, parent + ) + +def check_extras(dist, attr, value): + """Verify that extras_require mapping is valid""" + try: + for k,v in value.items(): + if ':' in k: + k,m = k.split(':',1) + if pkg_resources.invalid_marker(m): + raise DistutilsSetupError("Invalid environment marker: "+m) + list(pkg_resources.parse_requirements(v)) + except (TypeError,ValueError,AttributeError): + raise DistutilsSetupError( + "'extras_require' must be a dictionary whose values are " + "strings or lists of strings containing valid project/version " + "requirement specifiers." + ) + +def assert_bool(dist, attr, value): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + tmpl = "{attr!r} must be a boolean value (got {value!r})" + raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) + + +def check_requirements(dist, attr, value): + """Verify that install_requires is a valid requirements list""" + try: + list(pkg_resources.parse_requirements(value)) + except (TypeError, ValueError) as error: + tmpl = ( + "{attr!r} must be a string or list of strings " + "containing valid project/version requirement specifiers; {error}" + ) + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + +def check_entry_points(dist, attr, value): + """Verify that entry_points map is parseable""" + try: + pkg_resources.EntryPoint.parse_map(value) + except ValueError as e: + raise DistutilsSetupError(e) + +def check_test_suite(dist, attr, value): + if not isinstance(value, six.string_types): + raise DistutilsSetupError("test_suite must be a string") + +def check_package_data(dist, attr, value): + """Verify that value is a dictionary of package names to glob lists""" + if isinstance(value,dict): + for k,v in value.items(): + if not isinstance(k,str): break + try: iter(v) + except TypeError: + break + else: + return + raise DistutilsSetupError( + attr+" must be a dictionary mapping package names to lists of " + "wildcard patterns" + ) + +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only " + ".-separated package names in setup.py", pkgname + ) + + +class Distribution(_Distribution): + """Distribution with support for features, tests, and package data + + This is an enhanced version of 'distutils.dist.Distribution' that + effectively adds the following new optional keyword arguments to 'setup()': + + 'install_requires' -- a string or sequence of strings specifying project + versions that the distribution requires when installed, in the format + used by 'pkg_resources.require()'. They will be installed + automatically when the package is installed. If you wish to use + packages that are not available in PyPI, or want to give your users an + alternate download location, you can add a 'find_links' option to the + '[easy_install]' section of your project's 'setup.cfg' file, and then + setuptools will scan the listed web pages for links that satisfy the + requirements. + + 'extras_require' -- a dictionary mapping names of optional "extras" to the + additional requirement(s) that using those extras incurs. For example, + this:: + + extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) + + indicates that the distribution can optionally provide an extra + capability called "reST", but it can only be used if docutils and + reSTedit are installed. If the user installs your package using + EasyInstall and requests one of your extras, the corresponding + additional requirements will be installed if needed. + + 'features' **deprecated** -- a dictionary mapping option names to + 'setuptools.Feature' + objects. Features are a portion of the distribution that can be + included or excluded based on user options, inter-feature dependencies, + and availability on the current system. Excluded features are omitted + from all setup commands, including source and binary distributions, so + you can create multiple distributions from the same source tree. + Feature names should be valid Python identifiers, except that they may + contain the '-' (minus) sign. Features can be included or excluded + via the command line options '--with-X' and '--without-X', where 'X' is + the name of the feature. Whether a feature is included by default, and + whether you are allowed to control this from the command line, is + determined by the Feature object. See the 'Feature' class for more + information. + + 'test_suite' -- the name of a test suite to run for the 'test' command. + If the user runs 'python setup.py test', the package will be installed, + and the named test suite will be run. The format is the same as + would be used on a 'unittest.py' command line. That is, it is the + dotted name of an object to import and call to generate a test suite. + + 'package_data' -- a dictionary mapping package names to lists of filenames + or globs to use to find data files contained in the named packages. + If the dictionary has filenames or globs listed under '""' (the empty + string), those names will be searched for in every package, in addition + to any names for the specific package. Data files found using these + names/globs will be installed along with the package, in the same + location as the package. Note that globs are allowed to reference + the contents of non-package subdirectories, as long as you use '/' as + a path separator. (Globs are automatically converted to + platform-specific paths at runtime.) + + In addition to these new keywords, this class also has several new methods + for manipulating the distribution's contents. For example, the 'include()' + and 'exclude()' methods can be thought of as in-place add and subtract + commands that add or remove packages, modules, extensions, and so on from + the distribution. They are used by the feature subsystem to configure the + distribution for the included and excluded features. + """ + + _patched_dist = None + + def patch_missing_pkg_info(self, attrs): + # Fake up a replacement for the data that would normally come from + # PKG-INFO, but which might not yet be built if this is a fresh + # checkout. + # + if not attrs or 'name' not in attrs or 'version' not in attrs: + return + key = pkg_resources.safe_name(str(attrs['name'])).lower() + dist = pkg_resources.working_set.by_key.get(key) + if dist is not None and not dist.has_metadata('PKG-INFO'): + dist._version = pkg_resources.safe_version(str(attrs['version'])) + self._patched_dist = dist + + def __init__(self, attrs=None): + have_package_data = hasattr(self, "package_data") + if not have_package_data: + self.package_data = {} + _attrs_dict = attrs or {} + if 'features' in _attrs_dict or 'require_features' in _attrs_dict: + Feature.warn_deprecated() + self.require_features = [] + self.features = {} + self.dist_files = [] + self.src_root = attrs and attrs.pop("src_root", None) + self.patch_missing_pkg_info(attrs) + # Make sure we have any eggs needed to interpret 'attrs' + if attrs is not None: + self.dependency_links = attrs.pop('dependency_links', []) + assert_string_list(self,'dependency_links',self.dependency_links) + if attrs and 'setup_requires' in attrs: + self.fetch_build_eggs(attrs['setup_requires']) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + vars(self).setdefault(ep.name, None) + _Distribution.__init__(self,attrs) + if isinstance(self.metadata.version, numbers.Number): + # Some people apparently take "version number" too literally :) + self.metadata.version = str(self.metadata.version) + + if self.metadata.version is not None: + try: + ver = packaging.version.Version(self.metadata.version) + normalized_version = str(ver) + if self.metadata.version != normalized_version: + warnings.warn( + "Normalizing '%s' to '%s'" % ( + self.metadata.version, + normalized_version, + ) + ) + self.metadata.version = normalized_version + except (packaging.version.InvalidVersion, TypeError): + warnings.warn( + "The version specified (%r) is an invalid version, this " + "may not work as expected with newer versions of " + "setuptools, pip, and PyPI. Please see PEP 440 for more " + "details." % self.metadata.version + ) + + def parse_command_line(self): + """Process features after parsing command line options""" + result = _Distribution.parse_command_line(self) + if self.features: + self._finalize_features() + return result + + def _feature_attrname(self,name): + """Convert feature name to corresponding option attribute name""" + return 'with_'+name.replace('-','_') + + def fetch_build_eggs(self, requires): + """Resolve pre-setup requirements""" + resolved_dists = pkg_resources.working_set.resolve( + pkg_resources.parse_requirements(requires), + installer=self.fetch_build_egg, + replace_conflicting=True, + ) + for dist in resolved_dists: + pkg_resources.working_set.add(dist, replace=True) + + def finalize_options(self): + _Distribution.finalize_options(self) + if self.features: + self._set_global_opts_from_features() + + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + value = getattr(self,ep.name,None) + if value is not None: + ep.require(installer=self.fetch_build_egg) + ep.load()(self, ep.name, value) + if getattr(self, 'convert_2to3_doctests', None): + # XXX may convert to set here when we can rely on set being builtin + self.convert_2to3_doctests = [os.path.abspath(p) for p in self.convert_2to3_doctests] + else: + self.convert_2to3_doctests = [] + + def get_egg_cache_dir(self): + egg_cache_dir = os.path.join(os.curdir, '.eggs') + if not os.path.exists(egg_cache_dir): + os.mkdir(egg_cache_dir) + windows_support.hide_file(egg_cache_dir) + readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') + with open(readme_txt_filename, 'w') as f: + f.write('This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n') + f.write('This directory caches those eggs to prevent ' + 'repeated downloads.\n\n') + f.write('However, it is safe to delete this directory.\n\n') + + return egg_cache_dir + + def fetch_build_egg(self, req): + """Fetch an egg needed for building""" + + try: + cmd = self._egg_fetcher + cmd.package_index.to_scan = [] + except AttributeError: + from setuptools.command.easy_install import easy_install + dist = self.__class__({'script_args':['easy_install']}) + dist.parse_config_files() + opts = dist.get_option_dict('easy_install') + keep = ( + 'find_links', 'site_dirs', 'index_url', 'optimize', + 'site_dirs', 'allow_hosts' + ) + for key in list(opts): + if key not in keep: + del opts[key] # don't use any other settings + if self.dependency_links: + links = self.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1].split() + links + opts['find_links'] = ('setup', links) + install_dir = self.get_egg_cache_dir() + cmd = easy_install( + dist, args=["x"], install_dir=install_dir, exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False, multi_version=True, no_report=True, user=False + ) + cmd.ensure_finalized() + self._egg_fetcher = cmd + return cmd.easy_install(req) + + def _set_global_opts_from_features(self): + """Add --with-X/--without-X options based on optional features""" + + go = [] + no = self.negative_opt.copy() + + for name,feature in self.features.items(): + self._set_feature(name,None) + feature.validate(self) + + if feature.optional: + descr = feature.description + incdef = ' (default)' + excdef='' + if not feature.include_by_default(): + excdef, incdef = incdef, excdef + + go.append(('with-'+name, None, 'include '+descr+incdef)) + go.append(('without-'+name, None, 'exclude '+descr+excdef)) + no['without-'+name] = 'with-'+name + + self.global_options = self.feature_options = go + self.global_options + self.negative_opt = self.feature_negopt = no + + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + + # First, flag all the enabled items (and thus their dependencies) + for name,feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name,1) + + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name,feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name,0) + + def get_command_class(self, command): + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + + for ep in pkg_resources.iter_entry_points('distutils.commands',command): + ep.require(installer=self.fetch_build_egg) + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass + else: + return _Distribution.get_command_class(self, command) + + def print_commands(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + # don't require extras as the commands won't be invoked + cmdclass = ep.resolve() + self.cmdclass[ep.name] = cmdclass + return _Distribution.print_commands(self) + + def get_command_list(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + # don't require extras as the commands won't be invoked + cmdclass = ep.resolve() + self.cmdclass[ep.name] = cmdclass + return _Distribution.get_command_list(self) + + def _set_feature(self,name,status): + """Set feature's inclusion status""" + setattr(self,self._feature_attrname(name),status) + + def feature_is_included(self,name): + """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" + return getattr(self,self._feature_attrname(name)) + + def include_feature(self,name): + """Request inclusion of feature named 'name'""" + + if self.feature_is_included(name)==0: + descr = self.features[name].description + raise DistutilsOptionError( + descr + " is required, but was excluded or is not available" + ) + self.features[name].include_in(self) + self._set_feature(name,1) + + def include(self,**attrs): + """Add items to distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would add 'x' to + the distribution's 'py_modules' attribute, if it was not already + there. + + Currently, this method only supports inclusion for attributes that are + lists or tuples. If you need to add support for adding to other + attributes in this or a subclass, you can add an '_include_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' + will try to call 'dist._include_foo({"bar":"baz"})', which can then + handle whatever special inclusion logic is needed. + """ + for k,v in attrs.items(): + include = getattr(self, '_include_'+k, None) + if include: + include(v) + else: + self._include_misc(k,v) + + def exclude_package(self,package): + """Remove packages, modules, and extensions in named package""" + + pfx = package+'.' + if self.packages: + self.packages = [ + p for p in self.packages + if p != package and not p.startswith(pfx) + ] + + if self.py_modules: + self.py_modules = [ + p for p in self.py_modules + if p != package and not p.startswith(pfx) + ] + + if self.ext_modules: + self.ext_modules = [ + p for p in self.ext_modules + if p.name != package and not p.name.startswith(pfx) + ] + + def has_contents_for(self,package): + """Return true if 'exclude_package(package)' would do something""" + + pfx = package+'.' + + for p in self.iter_distribution_names(): + if p==package or p.startswith(pfx): + return True + + def _exclude_misc(self,name,value): + """Handle 'exclude()' for list/tuple attrs without a special handler""" + if not isinstance(value,sequence): + raise DistutilsSetupError( + "%s: setting must be a list or tuple (%r)" % (name, value) + ) + try: + old = getattr(self,name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is not None and not isinstance(old,sequence): + raise DistutilsSetupError( + name+": this setting cannot be changed via include/exclude" + ) + elif old: + setattr(self,name,[item for item in old if item not in value]) + + def _include_misc(self,name,value): + """Handle 'include()' for list/tuple attrs without a special handler""" + + if not isinstance(value,sequence): + raise DistutilsSetupError( + "%s: setting must be a list (%r)" % (name, value) + ) + try: + old = getattr(self,name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is None: + setattr(self,name,value) + elif not isinstance(old,sequence): + raise DistutilsSetupError( + name+": this setting cannot be changed via include/exclude" + ) + else: + setattr(self,name,old+[item for item in value if item not in old]) + + def exclude(self,**attrs): + """Remove items from distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from + the distribution's 'py_modules' attribute. Excluding packages uses + the 'exclude_package()' method, so all of the package's contained + packages, modules, and extensions are also excluded. + + Currently, this method only supports exclusion from attributes that are + lists or tuples. If you need to add support for excluding from other + attributes in this or a subclass, you can add an '_exclude_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' + will try to call 'dist._exclude_foo({"bar":"baz"})', which can then + handle whatever special exclusion logic is needed. + """ + for k,v in attrs.items(): + exclude = getattr(self, '_exclude_'+k, None) + if exclude: + exclude(v) + else: + self._exclude_misc(k,v) + + def _exclude_packages(self,packages): + if not isinstance(packages,sequence): + raise DistutilsSetupError( + "packages: setting must be a list or tuple (%r)" % (packages,) + ) + list(map(self.exclude_package, packages)) + + def _parse_command_opts(self, parser, args): + # Remove --with-X/--without-X options when processing command args + self.global_options = self.__class__.global_options + self.negative_opt = self.__class__.negative_opt + + # First, expand any aliases + command = args[0] + aliases = self.get_option_dict('aliases') + while command in aliases: + src,alias = aliases[command] + del aliases[command] # ensure each alias can expand only once! + import shlex + args[:1] = shlex.split(alias,True) + command = args[0] + + nargs = _Distribution._parse_command_opts(self, parser, args) + + # Handle commands that want to consume all remaining arguments + cmd_class = self.get_command_class(command) + if getattr(cmd_class,'command_consumes_arguments',None): + self.get_option_dict(command)['args'] = ("command line", nargs) + if nargs is not None: + return [] + + return nargs + + def get_cmdline_options(self): + """Return a '{cmd: {opt:val}}' map of all command-line options + + Option names are all long, but do not include the leading '--', and + contain dashes rather than underscores. If the option doesn't take + an argument (e.g. '--quiet'), the 'val' is 'None'. + + Note that options provided by config files are intentionally excluded. + """ + + d = {} + + for cmd,opts in self.command_options.items(): + + for opt,(src,val) in opts.items(): + + if src != "command line": + continue + + opt = opt.replace('_','-') + + if val==0: + cmdobj = self.get_command_obj(cmd) + neg_opt = self.negative_opt.copy() + neg_opt.update(getattr(cmdobj,'negative_opt',{})) + for neg,pos in neg_opt.items(): + if pos==opt: + opt=neg + val=None + break + else: + raise AssertionError("Shouldn't be able to get here") + + elif val==1: + val = None + + d.setdefault(cmd,{})[opt] = val + + return d + + def iter_distribution_names(self): + """Yield all packages, modules, and extension names in distribution""" + + for pkg in self.packages or (): + yield pkg + + for module in self.py_modules or (): + yield module + + for ext in self.ext_modules or (): + if isinstance(ext,tuple): + name, buildinfo = ext + else: + name = ext.name + if name.endswith('module'): + name = name[:-6] + yield name + + def handle_display_options(self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ + import sys + + if six.PY2 or self.help_commands: + return _Distribution.handle_display_options(self, option_order) + + # Stdout may be StringIO (e.g. in tests) + import io + if not isinstance(sys.stdout, io.TextIOWrapper): + return _Distribution.handle_display_options(self, option_order) + + # Don't wrap stdout if utf-8 is already the encoding. Provides + # workaround for #334. + if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): + return _Distribution.handle_display_options(self, option_order) + + # Print metadata in UTF-8 no matter the platform + encoding = sys.stdout.encoding + errors = sys.stdout.errors + newline = sys.platform != 'win32' and '\n' or None + line_buffering = sys.stdout.line_buffering + + sys.stdout = io.TextIOWrapper( + sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) + try: + return _Distribution.handle_display_options(self, option_order) + finally: + sys.stdout = io.TextIOWrapper( + sys.stdout.detach(), encoding, errors, newline, line_buffering) + + +# Install it throughout the distutils +for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = Distribution + + +class Feature: + """ + **deprecated** -- The `Feature` facility was never completely implemented + or supported, `has reported issues + <https://github.com/pypa/setuptools/issues/58>`_ and will be removed in + a future version. + + A subset of the distribution that can be excluded if unneeded/wanted + + Features are created using these keyword arguments: + + 'description' -- a short, human readable description of the feature, to + be used in error messages, and option help messages. + + 'standard' -- if true, the feature is included by default if it is + available on the current system. Otherwise, the feature is only + included if requested via a command line '--with-X' option, or if + another included feature requires it. The default setting is 'False'. + + 'available' -- if true, the feature is available for installation on the + current system. The default setting is 'True'. + + 'optional' -- if true, the feature's inclusion can be controlled from the + command line, using the '--with-X' or '--without-X' options. If + false, the feature's inclusion status is determined automatically, + based on 'availabile', 'standard', and whether any other feature + requires it. The default setting is 'True'. + + 'require_features' -- a string or sequence of strings naming features + that should also be included if this feature is included. Defaults to + empty list. May also contain 'Require' objects that should be + added/removed from the distribution. + + 'remove' -- a string or list of strings naming packages to be removed + from the distribution if this feature is *not* included. If the + feature *is* included, this argument is ignored. This argument exists + to support removing features that "crosscut" a distribution, such as + defining a 'tests' feature that removes all the 'tests' subpackages + provided by other features. The default for this argument is an empty + list. (Note: the named package(s) or modules must exist in the base + distribution when the 'setup()' function is initially called.) + + other keywords -- any other keyword arguments are saved, and passed to + the distribution's 'include()' and 'exclude()' methods when the + feature is included or excluded, respectively. So, for example, you + could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be + added or removed from the distribution as appropriate. + + A feature must include at least one 'requires', 'remove', or other + keyword argument. Otherwise, it can't affect the distribution in any way. + Note also that you can subclass 'Feature' to create your own specialized + feature types that modify the distribution in other ways when included or + excluded. See the docstrings for the various methods here for more detail. + Aside from the methods, the only feature attributes that distributions look + at are 'description' and 'optional'. + """ + + @staticmethod + def warn_deprecated(): + warnings.warn( + "Features are deprecated and will be removed in a future " + "version. See https://github.com/pypa/setuptools/issues/65.", + DeprecationWarning, + stacklevel=3, + ) + + def __init__(self, description, standard=False, available=True, + optional=True, require_features=(), remove=(), **extras): + self.warn_deprecated() + + self.description = description + self.standard = standard + self.available = available + self.optional = optional + if isinstance(require_features,(str,Require)): + require_features = require_features, + + self.require_features = [ + r for r in require_features if isinstance(r,str) + ] + er = [r for r in require_features if not isinstance(r,str)] + if er: extras['require_features'] = er + + if isinstance(remove,str): + remove = remove, + self.remove = remove + self.extras = extras + + if not remove and not require_features and not extras: + raise DistutilsSetupError( + "Feature %s: must define 'require_features', 'remove', or at least one" + " of 'packages', 'py_modules', etc." + ) + + def include_by_default(self): + """Should this feature be included by default?""" + return self.available and self.standard + + def include_in(self,dist): + + """Ensure feature and its requirements are included in distribution + + You may override this in a subclass to perform additional operations on + the distribution. Note that this method may be called more than once + per feature, and so should be idempotent. + + """ + + if not self.available: + raise DistutilsPlatformError( + self.description+" is required, " + "but is not available on this platform" + ) + + dist.include(**self.extras) + + for f in self.require_features: + dist.include_feature(f) + + def exclude_from(self,dist): + + """Ensure feature is excluded from distribution + + You may override this in a subclass to perform additional operations on + the distribution. This method will be called at most once per + feature, and only after all included features have been asked to + include themselves. + """ + + dist.exclude(**self.extras) + + if self.remove: + for item in self.remove: + dist.exclude_package(item) + + def validate(self,dist): + + """Verify that feature makes sense in context of distribution + + This method is called by the distribution just before it parses its + command line. It checks to ensure that the 'remove' attribute, if any, + contains only valid package/module names that are present in the base + distribution when 'setup()' is called. You may override it in a + subclass to perform any other required validation of the feature + against a target distribution. + """ + + for item in self.remove: + if not dist.has_contents_for(item): + raise DistutilsSetupError( + "%s wants to be able to remove %s, but the distribution" + " doesn't contain any packages or modules under %s" + % (self.description, item, item) + ) diff --git a/venv/lib/python3.5/site-packages/setuptools/extension.py b/venv/lib/python3.5/site-packages/setuptools/extension.py new file mode 100644 index 0000000..d10609b --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/extension.py @@ -0,0 +1,57 @@ +import sys +import re +import functools +import distutils.core +import distutils.errors +import distutils.extension + +from setuptools.extern.six.moves import map + +from .dist import _get_unpatched +from . import msvc9_support + +_Extension = _get_unpatched(distutils.core.Extension) + +msvc9_support.patch_for_specialized_compiler() + +def _have_cython(): + """ + Return True if Cython can be imported. + """ + cython_impl = 'Cython.Distutils.build_ext', + try: + # from (cython_impl) import build_ext + __import__(cython_impl, fromlist=['build_ext']).build_ext + return True + except Exception: + pass + return False + +# for compatibility +have_pyrex = _have_cython + + +class Extension(_Extension): + """Extension that uses '.c' files in place of '.pyx' files""" + + def _convert_pyx_sources_to_lang(self): + """ + Replace sources with .pyx extensions to sources with the target + language extension. This mechanism allows language authors to supply + pre-converted sources but to prefer the .pyx sources. + """ + if _have_cython(): + # the build has Cython, so allow it to compile the .pyx files + return + lang = self.language or '' + target_ext = '.cpp' if lang.lower() == 'c++' else '.c' + sub = functools.partial(re.sub, '.pyx$', target_ext) + self.sources = list(map(sub, self.sources)) + +class Library(Extension): + """Just like a regular Extension, but built as a library instead""" + +distutils.core.Extension = Extension +distutils.extension.Extension = Extension +if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = Extension diff --git a/venv/lib/python3.5/site-packages/setuptools/extern/__init__.py b/venv/lib/python3.5/site-packages/setuptools/extern/__init__.py new file mode 100644 index 0000000..6859aa5 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/extern/__init__.py @@ -0,0 +1,5 @@ +from pkg_resources.extern import VendorImporter + + +names = 'six', +VendorImporter(__name__, names, 'pkg_resources._vendor').install() diff --git a/venv/lib/python3.5/site-packages/setuptools/launch.py b/venv/lib/python3.5/site-packages/setuptools/launch.py new file mode 100644 index 0000000..06e15e1 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/launch.py @@ -0,0 +1,35 @@ +""" +Launch the Python script on the command line after +setuptools is bootstrapped via import. +""" + +# Note that setuptools gets imported implicitly by the +# invocation of this script using python -m setuptools.launch + +import tokenize +import sys + + +def run(): + """ + Run the script in sys.argv[1] as if it had + been invoked naturally. + """ + __builtins__ + script_name = sys.argv[1] + namespace = dict( + __file__ = script_name, + __name__ = '__main__', + __doc__ = None, + ) + sys.argv[:] = sys.argv[1:] + + open_ = getattr(tokenize, 'open', open) + script = open_(script_name).read() + norm_script = script.replace('\\r\\n', '\\n') + code = compile(norm_script, script_name, 'exec') + exec(code, namespace) + + +if __name__ == '__main__': + run() diff --git a/venv/lib/python3.5/site-packages/setuptools/lib2to3_ex.py b/venv/lib/python3.5/site-packages/setuptools/lib2to3_ex.py new file mode 100644 index 0000000..feef591 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/lib2to3_ex.py @@ -0,0 +1,58 @@ +""" +Customized Mixin2to3 support: + + - adds support for converting doctests + + +This module raises an ImportError on Python 2. +""" + +from distutils.util import Mixin2to3 as _Mixin2to3 +from distutils import log +from lib2to3.refactor import RefactoringTool, get_fixers_from_package +import setuptools + +class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): + log.error(msg, *args) + + def log_message(self, msg, *args): + log.info(msg, *args) + + def log_debug(self, msg, *args): + log.debug(msg, *args) + +class Mixin2to3(_Mixin2to3): + def run_2to3(self, files, doctests = False): + # See of the distribution option has been set, otherwise check the + # setuptools default. + if self.distribution.use_2to3 is not True: + return + if not files: + return + log.info("Fixing "+" ".join(files)) + self.__build_fixer_names() + self.__exclude_fixers() + if doctests: + if setuptools.run_2to3_on_doctests: + r = DistutilsRefactoringTool(self.fixer_names) + r.refactor(files, write=True, doctests_only=True) + else: + _Mixin2to3.run_2to3(self, files) + + def __build_fixer_names(self): + if self.fixer_names: return + self.fixer_names = [] + for p in setuptools.lib2to3_fixer_packages: + self.fixer_names.extend(get_fixers_from_package(p)) + if self.distribution.use_2to3_fixers is not None: + for p in self.distribution.use_2to3_fixers: + self.fixer_names.extend(get_fixers_from_package(p)) + + def __exclude_fixers(self): + excluded_fixers = getattr(self, 'exclude_fixers', []) + if self.distribution.use_2to3_exclude_fixers is not None: + excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers) + for fixer_name in excluded_fixers: + if fixer_name in self.fixer_names: + self.fixer_names.remove(fixer_name) diff --git a/venv/lib/python3.5/site-packages/setuptools/msvc9_support.py b/venv/lib/python3.5/site-packages/setuptools/msvc9_support.py new file mode 100644 index 0000000..9d86958 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/msvc9_support.py @@ -0,0 +1,63 @@ +try: + import distutils.msvc9compiler +except Exception: + pass + +unpatched = dict() + +def patch_for_specialized_compiler(): + """ + Patch functions in distutils.msvc9compiler to use the standalone compiler + build for Python (Windows only). Fall back to original behavior when the + standalone compiler is not available. + """ + if 'distutils' not in globals(): + # The module isn't available to be patched + return + + if unpatched: + # Already patched + return + + unpatched.update(vars(distutils.msvc9compiler)) + + distutils.msvc9compiler.find_vcvarsall = find_vcvarsall + distutils.msvc9compiler.query_vcvarsall = query_vcvarsall + +def find_vcvarsall(version): + Reg = distutils.msvc9compiler.Reg + VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' + key = VC_BASE % ('', version) + try: + # Per-user installs register the compiler path here + productdir = Reg.get_value(key, "installdir") + except KeyError: + try: + # All-user installs on a 64-bit system register here + key = VC_BASE % ('Wow6432Node\\', version) + productdir = Reg.get_value(key, "installdir") + except KeyError: + productdir = None + + if productdir: + import os + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + + return unpatched['find_vcvarsall'](version) + +def query_vcvarsall(version, *args, **kwargs): + try: + return unpatched['query_vcvarsall'](version, *args, **kwargs) + except distutils.errors.DistutilsPlatformError as exc: + if exc and "vcvarsall.bat" in exc.args[0]: + message = 'Microsoft Visual C++ %0.1f is required (%s).' % (version, exc.args[0]) + if int(version) == 9: + # This redirection link is maintained by Microsoft. + # Contact vspython@microsoft.com if it needs updating. + raise distutils.errors.DistutilsPlatformError( + message + ' Get it from http://aka.ms/vcpython27' + ) + raise distutils.errors.DistutilsPlatformError(message) + raise diff --git a/venv/lib/python3.5/site-packages/setuptools/package_index.py b/venv/lib/python3.5/site-packages/setuptools/package_index.py new file mode 100644 index 0000000..c53343e --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/package_index.py @@ -0,0 +1,1069 @@ +"""PyPI and direct package downloading""" +import sys +import os +import re +import shutil +import socket +import base64 +import hashlib +import itertools +from functools import wraps + +try: + from urllib.parse import splituser +except ImportError: + from urllib2 import splituser + +from setuptools.extern import six +from setuptools.extern.six.moves import urllib, http_client, configparser, map + +from pkg_resources import ( + CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, + require, Environment, find_distributions, safe_name, safe_version, + to_filename, Requirement, DEVELOP_DIST, +) +from setuptools import ssl_support +from distutils import log +from distutils.errors import DistutilsError +from fnmatch import translate +from setuptools.py26compat import strip_fragment +from setuptools.py27compat import get_all_headers + +EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') +HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) +# this is here to fix emacs' cruddy broken syntax highlighting +PYPI_MD5 = re.compile( + '<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a (?:title="MD5 hash"\n\s+)' + 'href="[^?]+\?:action=show_md5&digest=([0-9a-f]{32})">md5</a>\\)' +) +URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match +EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() + +__all__ = [ + 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst', + 'interpret_distro_name', +] + +_SOCKET_TIMEOUT = 15 + +def parse_bdist_wininst(name): + """Return (base,pyversion) or (None,None) for possible .exe name""" + + lower = name.lower() + base, py_ver, plat = None, None, None + + if lower.endswith('.exe'): + if lower.endswith('.win32.exe'): + base = name[:-10] + plat = 'win32' + elif lower.startswith('.win32-py',-16): + py_ver = name[-7:-4] + base = name[:-16] + plat = 'win32' + elif lower.endswith('.win-amd64.exe'): + base = name[:-14] + plat = 'win-amd64' + elif lower.startswith('.win-amd64-py',-20): + py_ver = name[-7:-4] + base = name[:-20] + plat = 'win-amd64' + return base,py_ver,plat + + +def egg_info_for_url(url): + parts = urllib.parse.urlparse(url) + scheme, server, path, parameters, query, fragment = parts + base = urllib.parse.unquote(path.split('/')[-1]) + if server=='sourceforge.net' and base=='download': # XXX Yuck + base = urllib.parse.unquote(path.split('/')[-2]) + if '#' in base: base, fragment = base.split('#',1) + return base,fragment + +def distros_for_url(url, metadata=None): + """Yield egg or source distribution objects that might be found at a URL""" + base, fragment = egg_info_for_url(url) + for dist in distros_for_location(url, base, metadata): yield dist + if fragment: + match = EGG_FRAGMENT.match(fragment) + if match: + for dist in interpret_distro_name( + url, match.group(1), metadata, precedence = CHECKOUT_DIST + ): + yield dist + +def distros_for_location(location, basename, metadata=None): + """Yield egg or source distribution objects based on basename""" + if basename.endswith('.egg.zip'): + basename = basename[:-4] # strip the .zip + if basename.endswith('.egg') and '-' in basename: + # only one, unambiguous interpretation + return [Distribution.from_location(location, basename, metadata)] + if basename.endswith('.exe'): + win_base, py_ver, platform = parse_bdist_wininst(basename) + if win_base is not None: + return interpret_distro_name( + location, win_base, metadata, py_ver, BINARY_DIST, platform + ) + # Try source distro extensions (.zip, .tgz, etc.) + # + for ext in EXTENSIONS: + if basename.endswith(ext): + basename = basename[:-len(ext)] + return interpret_distro_name(location, basename, metadata) + return [] # no extension matched + +def distros_for_filename(filename, metadata=None): + """Yield possible egg or source distribution objects based on a filename""" + return distros_for_location( + normalize_path(filename), os.path.basename(filename), metadata + ) + + +def interpret_distro_name( + location, basename, metadata, py_version=None, precedence=SOURCE_DIST, + platform=None + ): + """Generate alternative interpretations of a source distro name + + Note: if `location` is a filesystem filename, you should call + ``pkg_resources.normalize_path()`` on it before passing it to this + routine! + """ + # Generate alternative interpretations of a source distro name + # Because some packages are ambiguous as to name/versions split + # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc. + # So, we generate each possible interepretation (e.g. "adns, python-1.1.0" + # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice, + # the spurious interpretations should be ignored, because in the event + # there's also an "adns" package, the spurious "python-1.1.0" version will + # compare lower than any numeric version number, and is therefore unlikely + # to match a request for it. It's still a potential problem, though, and + # in the long run PyPI and the distutils should go for "safe" names and + # versions in distribution archive names (sdist and bdist). + + parts = basename.split('-') + if not py_version and any(re.match('py\d\.\d$', p) for p in parts[2:]): + # it is a bdist_dumb, not an sdist -- bail out + return + + for p in range(1,len(parts)+1): + yield Distribution( + location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), + py_version=py_version, precedence = precedence, + platform = platform + ) + +# From Python 2.7 docs +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in six.moves.filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element + +def unique_values(func): + """ + Wrap a function returning an iterable such that the resulting iterable + only ever yields unique items. + """ + @wraps(func) + def wrapper(*args, **kwargs): + return unique_everseen(func(*args, **kwargs)) + return wrapper + +REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) +# this line is here to fix emacs' cruddy broken syntax highlighting + +@unique_values +def find_external_links(url, page): + """Find rel="homepage" and rel="download" links in `page`, yielding URLs""" + + for match in REL.finditer(page): + tag, rel = match.groups() + rels = set(map(str.strip, rel.lower().split(','))) + if 'homepage' in rels or 'download' in rels: + for match in HREF.finditer(tag): + yield urllib.parse.urljoin(url, htmldecode(match.group(1))) + + for tag in ("<th>Home Page", "<th>Download URL"): + pos = page.find(tag) + if pos!=-1: + match = HREF.search(page,pos) + if match: + yield urllib.parse.urljoin(url, htmldecode(match.group(1))) + +user_agent = "Python-urllib/%s setuptools/%s" % ( + sys.version[:3], require('setuptools')[0].version +) + +class ContentChecker(object): + """ + A null content checker that defines the interface for checking content + """ + def feed(self, block): + """ + Feed a block of data to the hash. + """ + return + + def is_valid(self): + """ + Check the hash. Return False if validation fails. + """ + return True + + def report(self, reporter, template): + """ + Call reporter with information about the checker (hash name) + substituted into the template. + """ + return + +class HashChecker(ContentChecker): + pattern = re.compile( + r'(?P<hash_name>sha1|sha224|sha384|sha256|sha512|md5)=' + r'(?P<expected>[a-f0-9]+)' + ) + + def __init__(self, hash_name, expected): + self.hash_name = hash_name + self.hash = hashlib.new(hash_name) + self.expected = expected + + @classmethod + def from_url(cls, url): + "Construct a (possibly null) ContentChecker from a URL" + fragment = urllib.parse.urlparse(url)[-1] + if not fragment: + return ContentChecker() + match = cls.pattern.search(fragment) + if not match: + return ContentChecker() + return cls(**match.groupdict()) + + def feed(self, block): + self.hash.update(block) + + def is_valid(self): + return self.hash.hexdigest() == self.expected + + def report(self, reporter, template): + msg = template % self.hash_name + return reporter(msg) + + +class PackageIndex(Environment): + """A distribution index that scans web pages for download URLs""" + + def __init__( + self, index_url="https://pypi.python.org/simple", hosts=('*',), + ca_bundle=None, verify_ssl=True, *args, **kw + ): + Environment.__init__(self,*args,**kw) + self.index_url = index_url + "/"[:not index_url.endswith('/')] + self.scanned_urls = {} + self.fetched_urls = {} + self.package_pages = {} + self.allows = re.compile('|'.join(map(translate,hosts))).match + self.to_scan = [] + if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()): + self.opener = ssl_support.opener_for(ca_bundle) + else: self.opener = urllib.request.urlopen + + def process_url(self, url, retrieve=False): + """Evaluate a URL as a possible download, and maybe retrieve it""" + if url in self.scanned_urls and not retrieve: + return + self.scanned_urls[url] = True + if not URL_SCHEME(url): + self.process_filename(url) + return + else: + dists = list(distros_for_url(url)) + if dists: + if not self.url_ok(url): + return + self.debug("Found link: %s", url) + + if dists or not retrieve or url in self.fetched_urls: + list(map(self.add, dists)) + return # don't need the actual page + + if not self.url_ok(url): + self.fetched_urls[url] = True + return + + self.info("Reading %s", url) + self.fetched_urls[url] = True # prevent multiple fetch attempts + f = self.open_url(url, "Download error on %s: %%s -- Some packages may not be found!" % url) + if f is None: return + self.fetched_urls[f.url] = True + if 'html' not in f.headers.get('content-type', '').lower(): + f.close() # not html, we can't process it + return + + base = f.url # handle redirects + page = f.read() + if not isinstance(page, str): # We are in Python 3 and got bytes. We want str. + if isinstance(f, urllib.error.HTTPError): + # Errors have no charset, assume latin1: + charset = 'latin-1' + else: + charset = f.headers.get_param('charset') or 'latin-1' + page = page.decode(charset, "ignore") + f.close() + for match in HREF.finditer(page): + link = urllib.parse.urljoin(base, htmldecode(match.group(1))) + self.process_url(link) + if url.startswith(self.index_url) and getattr(f,'code',None)!=404: + page = self.process_index(url, page) + + def process_filename(self, fn, nested=False): + # process filenames or directories + if not os.path.exists(fn): + self.warn("Not found: %s", fn) + return + + if os.path.isdir(fn) and not nested: + path = os.path.realpath(fn) + for item in os.listdir(path): + self.process_filename(os.path.join(path,item), True) + + dists = distros_for_filename(fn) + if dists: + self.debug("Found: %s", fn) + list(map(self.add, dists)) + + def url_ok(self, url, fatal=False): + s = URL_SCHEME(url) + if (s and s.group(1).lower()=='file') or self.allows(urllib.parse.urlparse(url)[1]): + return True + msg = ("\nNote: Bypassing %s (disallowed host; see " + "http://bit.ly/1dg9ijs for details).\n") + if fatal: + raise DistutilsError(msg % url) + else: + self.warn(msg, url) + + def scan_egg_links(self, search_path): + dirs = filter(os.path.isdir, search_path) + egg_links = ( + (path, entry) + for path in dirs + for entry in os.listdir(path) + if entry.endswith('.egg-link') + ) + list(itertools.starmap(self.scan_egg_link, egg_links)) + + def scan_egg_link(self, path, entry): + with open(os.path.join(path, entry)) as raw_lines: + # filter non-empty lines + lines = list(filter(None, map(str.strip, raw_lines))) + + if len(lines) != 2: + # format is not recognized; punt + return + + egg_path, setup_path = lines + + for dist in find_distributions(os.path.join(path, egg_path)): + dist.location = os.path.join(path, *lines) + dist.precedence = SOURCE_DIST + self.add(dist) + + def process_index(self,url,page): + """Process the contents of a PyPI page""" + def scan(link): + # Process a URL to see if it's for a package page + if link.startswith(self.index_url): + parts = list(map( + urllib.parse.unquote, link[len(self.index_url):].split('/') + )) + if len(parts)==2 and '#' not in parts[1]: + # it's a package page, sanitize and index it + pkg = safe_name(parts[0]) + ver = safe_version(parts[1]) + self.package_pages.setdefault(pkg.lower(),{})[link] = True + return to_filename(pkg), to_filename(ver) + return None, None + + # process an index page into the package-page index + for match in HREF.finditer(page): + try: + scan(urllib.parse.urljoin(url, htmldecode(match.group(1)))) + except ValueError: + pass + + pkg, ver = scan(url) # ensure this page is in the page index + if pkg: + # process individual package page + for new_url in find_external_links(url, page): + # Process the found URL + base, frag = egg_info_for_url(new_url) + if base.endswith('.py') and not frag: + if ver: + new_url+='#egg=%s-%s' % (pkg,ver) + else: + self.need_version_info(url) + self.scan_url(new_url) + + return PYPI_MD5.sub( + lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1,3,2), page + ) + else: + return "" # no sense double-scanning non-package pages + + def need_version_info(self, url): + self.scan_all( + "Page at %s links to .py file(s) without version info; an index " + "scan is required.", url + ) + + def scan_all(self, msg=None, *args): + if self.index_url not in self.fetched_urls: + if msg: self.warn(msg,*args) + self.info( + "Scanning index of all packages (this may take a while)" + ) + self.scan_url(self.index_url) + + def find_packages(self, requirement): + self.scan_url(self.index_url + requirement.unsafe_name+'/') + + if not self.package_pages.get(requirement.key): + # Fall back to safe version of the name + self.scan_url(self.index_url + requirement.project_name+'/') + + if not self.package_pages.get(requirement.key): + # We couldn't find the target package, so search the index page too + self.not_found_in_index(requirement) + + for url in list(self.package_pages.get(requirement.key,())): + # scan each page that might be related to the desired package + self.scan_url(url) + + def obtain(self, requirement, installer=None): + self.prescan() + self.find_packages(requirement) + for dist in self[requirement.key]: + if dist in requirement: + return dist + self.debug("%s does not match %s", requirement, dist) + return super(PackageIndex, self).obtain(requirement,installer) + + def check_hash(self, checker, filename, tfp): + """ + checker is a ContentChecker + """ + checker.report(self.debug, + "Validating %%s checksum for %s" % filename) + if not checker.is_valid(): + tfp.close() + os.unlink(filename) + raise DistutilsError( + "%s validation failed for %s; " + "possible download problem?" % ( + checker.hash.name, os.path.basename(filename)) + ) + + def add_find_links(self, urls): + """Add `urls` to the list that will be prescanned for searches""" + for url in urls: + if ( + self.to_scan is None # if we have already "gone online" + or not URL_SCHEME(url) # or it's a local file/directory + or url.startswith('file:') + or list(distros_for_url(url)) # or a direct package link + ): + # then go ahead and process it now + self.scan_url(url) + else: + # otherwise, defer retrieval till later + self.to_scan.append(url) + + def prescan(self): + """Scan urls scheduled for prescanning (e.g. --find-links)""" + if self.to_scan: + list(map(self.scan_url, self.to_scan)) + self.to_scan = None # from now on, go ahead and process immediately + + def not_found_in_index(self, requirement): + if self[requirement.key]: # we've seen at least one distro + meth, msg = self.info, "Couldn't retrieve index page for %r" + else: # no distros seen for this name, might be misspelled + meth, msg = (self.warn, + "Couldn't find index page for %r (maybe misspelled?)") + meth(msg, requirement.unsafe_name) + self.scan_all() + + def download(self, spec, tmpdir): + """Locate and/or download `spec` to `tmpdir`, returning a local path + + `spec` may be a ``Requirement`` object, or a string containing a URL, + an existing local filename, or a project/version requirement spec + (i.e. the string form of a ``Requirement`` object). If it is the URL + of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one + that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is + automatically created alongside the downloaded file. + + If `spec` is a ``Requirement`` object or a string containing a + project/version requirement spec, this method returns the location of + a matching distribution (possibly after downloading it to `tmpdir`). + If `spec` is a locally existing file or directory name, it is simply + returned unchanged. If `spec` is a URL, it is downloaded to a subpath + of `tmpdir`, and the local filename is returned. Various errors may be + raised if a problem occurs during downloading. + """ + if not isinstance(spec,Requirement): + scheme = URL_SCHEME(spec) + if scheme: + # It's a url, download it to tmpdir + found = self._download_url(scheme.group(1), spec, tmpdir) + base, fragment = egg_info_for_url(spec) + if base.endswith('.py'): + found = self.gen_setup(found,fragment,tmpdir) + return found + elif os.path.exists(spec): + # Existing file or directory, just return it + return spec + else: + try: + spec = Requirement.parse(spec) + except ValueError: + raise DistutilsError( + "Not a URL, existing file, or requirement spec: %r" % + (spec,) + ) + return getattr(self.fetch_distribution(spec, tmpdir),'location',None) + + def fetch_distribution( + self, requirement, tmpdir, force_scan=False, source=False, + develop_ok=False, local_index=None + ): + """Obtain a distribution suitable for fulfilling `requirement` + + `requirement` must be a ``pkg_resources.Requirement`` instance. + If necessary, or if the `force_scan` flag is set, the requirement is + searched for in the (online) package index as well as the locally + installed packages. If a distribution matching `requirement` is found, + the returned distribution's ``location`` is the value you would have + gotten from calling the ``download()`` method with the matching + distribution's URL or filename. If no matching distribution is found, + ``None`` is returned. + + If the `source` flag is set, only source distributions and source + checkout links will be considered. Unless the `develop_ok` flag is + set, development and system eggs (i.e., those using the ``.egg-info`` + format) will be ignored. + """ + # process a Requirement + self.info("Searching for %s", requirement) + skipped = {} + dist = None + + def find(req, env=None): + if env is None: + env = self + # Find a matching distribution; may be called more than once + + for dist in env[req.key]: + + if dist.precedence==DEVELOP_DIST and not develop_ok: + if dist not in skipped: + self.warn("Skipping development or system egg: %s",dist) + skipped[dist] = 1 + continue + + if dist in req and (dist.precedence<=SOURCE_DIST or not source): + return dist + + if force_scan: + self.prescan() + self.find_packages(requirement) + dist = find(requirement) + + if local_index is not None: + dist = dist or find(requirement, local_index) + + if dist is None: + if self.to_scan is not None: + self.prescan() + dist = find(requirement) + + if dist is None and not force_scan: + self.find_packages(requirement) + dist = find(requirement) + + if dist is None: + self.warn( + "No local packages or download links found for %s%s", + (source and "a source distribution of " or ""), + requirement, + ) + else: + self.info("Best match: %s", dist) + return dist.clone(location=self.download(dist.location, tmpdir)) + + def fetch(self, requirement, tmpdir, force_scan=False, source=False): + """Obtain a file suitable for fulfilling `requirement` + + DEPRECATED; use the ``fetch_distribution()`` method now instead. For + backward compatibility, this routine is identical but returns the + ``location`` of the downloaded distribution instead of a distribution + object. + """ + dist = self.fetch_distribution(requirement,tmpdir,force_scan,source) + if dist is not None: + return dist.location + return None + + def gen_setup(self, filename, fragment, tmpdir): + match = EGG_FRAGMENT.match(fragment) + dists = match and [ + d for d in + interpret_distro_name(filename, match.group(1), None) if d.version + ] or [] + + if len(dists)==1: # unambiguous ``#egg`` fragment + basename = os.path.basename(filename) + + # Make sure the file has been downloaded to the temp dir. + if os.path.dirname(filename) != tmpdir: + dst = os.path.join(tmpdir, basename) + from setuptools.command.easy_install import samefile + if not samefile(filename, dst): + shutil.copy2(filename, dst) + filename=dst + + with open(os.path.join(tmpdir, 'setup.py'), 'w') as file: + file.write( + "from setuptools import setup\n" + "setup(name=%r, version=%r, py_modules=[%r])\n" + % ( + dists[0].project_name, dists[0].version, + os.path.splitext(basename)[0] + ) + ) + return filename + + elif match: + raise DistutilsError( + "Can't unambiguously interpret project/version identifier %r; " + "any dashes in the name or version should be escaped using " + "underscores. %r" % (fragment,dists) + ) + else: + raise DistutilsError( + "Can't process plain .py files without an '#egg=name-version'" + " suffix to enable automatic setup script generation." + ) + + dl_blocksize = 8192 + def _download_to(self, url, filename): + self.info("Downloading %s", url) + # Download the file + fp, info = None, None + try: + checker = HashChecker.from_url(url) + fp = self.open_url(strip_fragment(url)) + if isinstance(fp, urllib.error.HTTPError): + raise DistutilsError( + "Can't download %s: %s %s" % (url, fp.code,fp.msg) + ) + headers = fp.info() + blocknum = 0 + bs = self.dl_blocksize + size = -1 + if "content-length" in headers: + # Some servers return multiple Content-Length headers :( + sizes = get_all_headers(headers, 'Content-Length') + size = max(map(int, sizes)) + self.reporthook(url, filename, blocknum, bs, size) + with open(filename,'wb') as tfp: + while True: + block = fp.read(bs) + if block: + checker.feed(block) + tfp.write(block) + blocknum += 1 + self.reporthook(url, filename, blocknum, bs, size) + else: + break + self.check_hash(checker, filename, tfp) + return headers + finally: + if fp: fp.close() + + def reporthook(self, url, filename, blocknum, blksize, size): + pass # no-op + + def open_url(self, url, warning=None): + if url.startswith('file:'): + return local_open(url) + try: + return open_with_auth(url, self.opener) + except (ValueError, http_client.InvalidURL) as v: + msg = ' '.join([str(arg) for arg in v.args]) + if warning: + self.warn(warning, msg) + else: + raise DistutilsError('%s %s' % (url, msg)) + except urllib.error.HTTPError as v: + return v + except urllib.error.URLError as v: + if warning: + self.warn(warning, v.reason) + else: + raise DistutilsError("Download error for %s: %s" + % (url, v.reason)) + except http_client.BadStatusLine as v: + if warning: + self.warn(warning, v.line) + else: + raise DistutilsError( + '%s returned a bad status line. The server might be ' + 'down, %s' % + (url, v.line) + ) + except http_client.HTTPException as v: + if warning: + self.warn(warning, v) + else: + raise DistutilsError("Download error for %s: %s" + % (url, v)) + + def _download_url(self, scheme, url, tmpdir): + # Determine download filename + # + name, fragment = egg_info_for_url(url) + if name: + while '..' in name: + name = name.replace('..','.').replace('\\','_') + else: + name = "__downloaded__" # default if URL has no path contents + + if name.endswith('.egg.zip'): + name = name[:-4] # strip the extra .zip before download + + filename = os.path.join(tmpdir,name) + + # Download the file + # + if scheme=='svn' or scheme.startswith('svn+'): + return self._download_svn(url, filename) + elif scheme=='git' or scheme.startswith('git+'): + return self._download_git(url, filename) + elif scheme.startswith('hg+'): + return self._download_hg(url, filename) + elif scheme=='file': + return urllib.request.url2pathname(urllib.parse.urlparse(url)[2]) + else: + self.url_ok(url, True) # raises error if not allowed + return self._attempt_download(url, filename) + + def scan_url(self, url): + self.process_url(url, True) + + def _attempt_download(self, url, filename): + headers = self._download_to(url, filename) + if 'html' in headers.get('content-type','').lower(): + return self._download_html(url, headers, filename) + else: + return filename + + def _download_html(self, url, headers, filename): + file = open(filename) + for line in file: + if line.strip(): + # Check for a subversion index page + if re.search(r'<title>([^- ]+ - )?Revision \d+:', line): + # it's a subversion index page: + file.close() + os.unlink(filename) + return self._download_svn(url, filename) + break # not an index page + file.close() + os.unlink(filename) + raise DistutilsError("Unexpected HTML page found at "+url) + + def _download_svn(self, url, filename): + url = url.split('#',1)[0] # remove any fragment for svn's sake + creds = '' + if url.lower().startswith('svn:') and '@' in url: + scheme, netloc, path, p, q, f = urllib.parse.urlparse(url) + if not netloc and path.startswith('//') and '/' in path[2:]: + netloc, path = path[2:].split('/',1) + auth, host = splituser(netloc) + if auth: + if ':' in auth: + user, pw = auth.split(':',1) + creds = " --username=%s --password=%s" % (user, pw) + else: + creds = " --username="+auth + netloc = host + parts = scheme, netloc, url, p, q, f + url = urllib.parse.urlunparse(parts) + self.info("Doing subversion checkout from %s to %s", url, filename) + os.system("svn checkout%s -q %s %s" % (creds, url, filename)) + return filename + + @staticmethod + def _vcs_split_rev_from_url(url, pop_prefix=False): + scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) + + scheme = scheme.split('+', 1)[-1] + + # Some fragment identification fails + path = path.split('#',1)[0] + + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + + # Also, discard fragment + url = urllib.parse.urlunsplit((scheme, netloc, path, query, '')) + + return url, rev + + def _download_git(self, url, filename): + filename = filename.split('#',1)[0] + url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) + + self.info("Doing git clone from %s to %s", url, filename) + os.system("git clone --quiet %s %s" % (url, filename)) + + if rev is not None: + self.info("Checking out %s", rev) + os.system("(cd %s && git checkout --quiet %s)" % ( + filename, + rev, + )) + + return filename + + def _download_hg(self, url, filename): + filename = filename.split('#',1)[0] + url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) + + self.info("Doing hg clone from %s to %s", url, filename) + os.system("hg clone --quiet %s %s" % (url, filename)) + + if rev is not None: + self.info("Updating to %s", rev) + os.system("(cd %s && hg up -C -r %s >&-)" % ( + filename, + rev, + )) + + return filename + + def debug(self, msg, *args): + log.debug(msg, *args) + + def info(self, msg, *args): + log.info(msg, *args) + + def warn(self, msg, *args): + log.warn(msg, *args) + +# This pattern matches a character entity reference (a decimal numeric +# references, a hexadecimal numeric reference, or a named reference). +entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub + +def uchr(c): + if not isinstance(c, int): + return c + if c>255: return six.unichr(c) + return chr(c) + +def decode_entity(match): + what = match.group(1) + if what.startswith('#x'): + what = int(what[2:], 16) + elif what.startswith('#'): + what = int(what[1:]) + else: + what = six.moves.html_entities.name2codepoint.get(what, match.group(0)) + return uchr(what) + +def htmldecode(text): + """Decode HTML entities in the given text.""" + return entity_sub(decode_entity, text) + +def socket_timeout(timeout=15): + def _socket_timeout(func): + def _socket_timeout(*args, **kwargs): + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + try: + return func(*args, **kwargs) + finally: + socket.setdefaulttimeout(old_timeout) + return _socket_timeout + return _socket_timeout + +def _encode_auth(auth): + """ + A function compatible with Python 2.3-3.3 that will encode + auth from a URL suitable for an HTTP header. + >>> str(_encode_auth('username%3Apassword')) + 'dXNlcm5hbWU6cGFzc3dvcmQ=' + + Long auth strings should not cause a newline to be inserted. + >>> long_auth = 'username:' + 'password'*10 + >>> chr(10) in str(_encode_auth(long_auth)) + False + """ + auth_s = urllib.parse.unquote(auth) + # convert to bytes + auth_bytes = auth_s.encode() + # use the legacy interface for Python 2.3 support + encoded_bytes = base64.encodestring(auth_bytes) + # convert back to a string + encoded = encoded_bytes.decode() + # strip the trailing carriage return + return encoded.replace('\n','') + +class Credential(object): + """ + A username/password pair. Use like a namedtuple. + """ + def __init__(self, username, password): + self.username = username + self.password = password + + def __iter__(self): + yield self.username + yield self.password + + def __str__(self): + return '%(username)s:%(password)s' % vars(self) + +class PyPIConfig(configparser.RawConfigParser): + + def __init__(self): + """ + Load from ~/.pypirc + """ + defaults = dict.fromkeys(['username', 'password', 'repository'], '') + configparser.RawConfigParser.__init__(self, defaults) + + rc = os.path.join(os.path.expanduser('~'), '.pypirc') + if os.path.exists(rc): + self.read(rc) + + @property + def creds_by_repository(self): + sections_with_repositories = [ + section for section in self.sections() + if self.get(section, 'repository').strip() + ] + + return dict(map(self._get_repo_cred, sections_with_repositories)) + + def _get_repo_cred(self, section): + repo = self.get(section, 'repository').strip() + return repo, Credential( + self.get(section, 'username').strip(), + self.get(section, 'password').strip(), + ) + + def find_credential(self, url): + """ + If the URL indicated appears to be a repository defined in this + config, return the credential for that repository. + """ + for repository, cred in self.creds_by_repository.items(): + if url.startswith(repository): + return cred + + +def open_with_auth(url, opener=urllib.request.urlopen): + """Open a urllib2 request, handling HTTP authentication""" + + scheme, netloc, path, params, query, frag = urllib.parse.urlparse(url) + + # Double scheme does not raise on Mac OS X as revealed by a + # failing test. We would expect "nonnumeric port". Refs #20. + if netloc.endswith(':'): + raise http_client.InvalidURL("nonnumeric port: ''") + + if scheme in ('http', 'https'): + auth, host = splituser(netloc) + else: + auth = None + + if not auth: + cred = PyPIConfig().find_credential(url) + if cred: + auth = str(cred) + info = cred.username, url + log.info('Authenticating as %s for %s (from .pypirc)' % info) + + if auth: + auth = "Basic " + _encode_auth(auth) + parts = scheme, host, path, params, query, frag + new_url = urllib.parse.urlunparse(parts) + request = urllib.request.Request(new_url) + request.add_header("Authorization", auth) + else: + request = urllib.request.Request(url) + + request.add_header('User-Agent', user_agent) + fp = opener(request) + + if auth: + # Put authentication info back into request URL if same host, + # so that links found on the page will work + s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url) + if s2==scheme and h2==host: + parts = s2, netloc, path2, param2, query2, frag2 + fp.url = urllib.parse.urlunparse(parts) + + return fp + +# adding a timeout to avoid freezing package_index +open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) + + +def fix_sf_url(url): + return url # backward compatibility + +def local_open(url): + """Read a local path, with special support for directories""" + scheme, server, path, param, query, frag = urllib.parse.urlparse(url) + filename = urllib.request.url2pathname(path) + if os.path.isfile(filename): + return urllib.request.urlopen(url) + elif path.endswith('/') and os.path.isdir(filename): + files = [] + for f in os.listdir(filename): + filepath = os.path.join(filename, f) + if f == 'index.html': + with open(filepath, 'r') as fp: + body = fp.read() + break + elif os.path.isdir(filepath): + f += '/' + files.append('<a href="{name}">{name}</a>'.format(name=f)) + else: + tmpl = ("<html><head><title>{url}" + "{files}") + body = tmpl.format(url=url, files='\n'.join(files)) + status, message = 200, "OK" + else: + status, message, body = 404, "Path not found", "Not found" + + headers = {'content-type': 'text/html'} + body_stream = six.StringIO(body) + return urllib.error.HTTPError(url, status, message, headers, body_stream) diff --git a/venv/lib/python3.5/site-packages/setuptools/py26compat.py b/venv/lib/python3.5/site-packages/setuptools/py26compat.py new file mode 100644 index 0000000..e52bd85 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/py26compat.py @@ -0,0 +1,22 @@ +""" +Compatibility Support for Python 2.6 and earlier +""" + +import sys + +try: + from urllib.parse import splittag +except ImportError: + from urllib import splittag + +def strip_fragment(url): + """ + In `Python 8280 `_, Python 2.7 and + later was patched to disregard the fragment when making URL requests. + Do the same for Python 2.6 and earlier. + """ + url, fragment = splittag(url) + return url + +if sys.version_info >= (2,7): + strip_fragment = lambda x: x diff --git a/venv/lib/python3.5/site-packages/setuptools/py27compat.py b/venv/lib/python3.5/site-packages/setuptools/py27compat.py new file mode 100644 index 0000000..9d2886d --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/py27compat.py @@ -0,0 +1,15 @@ +""" +Compatibility Support for Python 2.7 and earlier +""" + +import sys + +def get_all_headers(message, key): + """ + Given an HTTPMessage, return all headers matching a given key. + """ + return message.get_all(key) + +if sys.version_info < (3,): + def get_all_headers(message, key): + return message.getheaders(key) diff --git a/venv/lib/python3.5/site-packages/setuptools/py31compat.py b/venv/lib/python3.5/site-packages/setuptools/py31compat.py new file mode 100644 index 0000000..8fe6dd9 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/py31compat.py @@ -0,0 +1,52 @@ +import sys +import unittest + +__all__ = ['get_config_vars', 'get_path'] + +try: + # Python 2.7 or >=3.2 + from sysconfig import get_config_vars, get_path +except ImportError: + from distutils.sysconfig import get_config_vars, get_python_lib + def get_path(name): + if name not in ('platlib', 'purelib'): + raise ValueError("Name must be purelib or platlib") + return get_python_lib(name=='platlib') + +try: + # Python >=3.2 + from tempfile import TemporaryDirectory +except ImportError: + import shutil + import tempfile + class TemporaryDirectory(object): + """ + Very simple temporary directory context manager. + Will try to delete afterward, but will also ignore OS and similar + errors on deletion. + """ + def __init__(self): + self.name = None # Handle mkdtemp raising an exception + self.name = tempfile.mkdtemp() + + def __enter__(self): + return self.name + + def __exit__(self, exctype, excvalue, exctrace): + try: + shutil.rmtree(self.name, True) + except OSError: #removal errors are not the only possible + pass + self.name = None + + +unittest_main = unittest.main + +_PY31 = (3, 1) <= sys.version_info[:2] < (3, 2) +if _PY31: + # on Python 3.1, translate testRunner==None to TextTestRunner + # for compatibility with Python 2.6, 2.7, and 3.2+ + def unittest_main(*args, **kwargs): + if 'testRunner' in kwargs and kwargs['testRunner'] is None: + kwargs['testRunner'] = unittest.TextTestRunner + return unittest.main(*args, **kwargs) diff --git a/venv/lib/python3.5/site-packages/setuptools/sandbox.py b/venv/lib/python3.5/site-packages/setuptools/sandbox.py new file mode 100644 index 0000000..23e296b --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/sandbox.py @@ -0,0 +1,496 @@ +import os +import sys +import tempfile +import operator +import functools +import itertools +import re +import contextlib +import pickle + +from setuptools.extern import six +from setuptools.extern.six.moves import builtins, map + +import pkg_resources + +if sys.platform.startswith('java'): + import org.python.modules.posix.PosixModule as _os +else: + _os = sys.modules[os.name] +try: + _file = file +except NameError: + _file = None +_open = open +from distutils.errors import DistutilsError +from pkg_resources import working_set + +__all__ = [ + "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", +] + +def _execfile(filename, globals, locals=None): + """ + Python 3 implementation of execfile. + """ + mode = 'rb' + with open(filename, mode) as stream: + script = stream.read() + # compile() function in Python 2.6 and 3.1 requires LF line endings. + if sys.version_info[:2] < (2, 7) or sys.version_info[:2] >= (3, 0) and sys.version_info[:2] < (3, 2): + script = script.replace(b'\r\n', b'\n') + script = script.replace(b'\r', b'\n') + if locals is None: + locals = globals + code = compile(script, filename, 'exec') + exec(code, globals, locals) + + +@contextlib.contextmanager +def save_argv(repl=None): + saved = sys.argv[:] + if repl is not None: + sys.argv[:] = repl + try: + yield saved + finally: + sys.argv[:] = saved + + +@contextlib.contextmanager +def save_path(): + saved = sys.path[:] + try: + yield saved + finally: + sys.path[:] = saved + + +@contextlib.contextmanager +def override_temp(replacement): + """ + Monkey-patch tempfile.tempdir with replacement, ensuring it exists + """ + if not os.path.isdir(replacement): + os.makedirs(replacement) + + saved = tempfile.tempdir + + tempfile.tempdir = replacement + + try: + yield + finally: + tempfile.tempdir = saved + + +@contextlib.contextmanager +def pushd(target): + saved = os.getcwd() + os.chdir(target) + try: + yield saved + finally: + os.chdir(saved) + + +class UnpickleableException(Exception): + """ + An exception representing another Exception that could not be pickled. + """ + @staticmethod + def dump(type, exc): + """ + Always return a dumped (pickled) type and exc. If exc can't be pickled, + wrap it in UnpickleableException first. + """ + try: + return pickle.dumps(type), pickle.dumps(exc) + except Exception: + # get UnpickleableException inside the sandbox + from setuptools.sandbox import UnpickleableException as cls + return cls.dump(cls, cls(repr(exc))) + + +class ExceptionSaver: + """ + A Context Manager that will save an exception, serialized, and restore it + later. + """ + def __enter__(self): + return self + + def __exit__(self, type, exc, tb): + if not exc: + return + + # dump the exception + self._saved = UnpickleableException.dump(type, exc) + self._tb = tb + + # suppress the exception + return True + + def resume(self): + "restore and re-raise any exception" + + if '_saved' not in vars(self): + return + + type, exc = map(pickle.loads, self._saved) + six.reraise(type, exc, self._tb) + + +@contextlib.contextmanager +def save_modules(): + """ + Context in which imported modules are saved. + + Translates exceptions internal to the context into the equivalent exception + outside the context. + """ + saved = sys.modules.copy() + with ExceptionSaver() as saved_exc: + yield saved + + sys.modules.update(saved) + # remove any modules imported since + del_modules = ( + mod_name for mod_name in sys.modules + if mod_name not in saved + # exclude any encodings modules. See #285 + and not mod_name.startswith('encodings.') + ) + _clear_modules(del_modules) + + saved_exc.resume() + + +def _clear_modules(module_names): + for mod_name in list(module_names): + del sys.modules[mod_name] + + +@contextlib.contextmanager +def save_pkg_resources_state(): + saved = pkg_resources.__getstate__() + try: + yield saved + finally: + pkg_resources.__setstate__(saved) + + +@contextlib.contextmanager +def setup_context(setup_dir): + temp_dir = os.path.join(setup_dir, 'temp') + with save_pkg_resources_state(): + with save_modules(): + hide_setuptools() + with save_path(): + with save_argv(): + with override_temp(temp_dir): + with pushd(setup_dir): + # ensure setuptools commands are available + __import__('setuptools') + yield + + +def _needs_hiding(mod_name): + """ + >>> _needs_hiding('setuptools') + True + >>> _needs_hiding('pkg_resources') + True + >>> _needs_hiding('setuptools_plugin') + False + >>> _needs_hiding('setuptools.__init__') + True + >>> _needs_hiding('distutils') + True + >>> _needs_hiding('os') + False + >>> _needs_hiding('Cython') + True + """ + pattern = re.compile('(setuptools|pkg_resources|distutils|Cython)(\.|$)') + return bool(pattern.match(mod_name)) + + +def hide_setuptools(): + """ + Remove references to setuptools' modules from sys.modules to allow the + invocation to import the most appropriate setuptools. This technique is + necessary to avoid issues such as #315 where setuptools upgrading itself + would fail to find a function declared in the metadata. + """ + modules = filter(_needs_hiding, sys.modules) + _clear_modules(modules) + + +def run_setup(setup_script, args): + """Run a distutils setup script, sandboxed in its directory""" + setup_dir = os.path.abspath(os.path.dirname(setup_script)) + with setup_context(setup_dir): + try: + sys.argv[:] = [setup_script]+list(args) + sys.path.insert(0, setup_dir) + # reset to include setup dir, w/clean callback list + working_set.__init__() + working_set.callbacks.append(lambda dist:dist.activate()) + def runner(): + ns = dict(__file__=setup_script, __name__='__main__') + _execfile(setup_script, ns) + DirectorySandbox(setup_dir).run(runner) + except SystemExit as v: + if v.args and v.args[0]: + raise + # Normal exit, just return + + +class AbstractSandbox: + """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" + + _active = False + + def __init__(self): + self._attrs = [ + name for name in dir(_os) + if not name.startswith('_') and hasattr(self,name) + ] + + def _copy(self, source): + for name in self._attrs: + setattr(os, name, getattr(source,name)) + + def run(self, func): + """Run 'func' under os sandboxing""" + try: + self._copy(self) + if _file: + builtins.file = self._file + builtins.open = self._open + self._active = True + return func() + finally: + self._active = False + if _file: + builtins.file = _file + builtins.open = _open + self._copy(_os) + + def _mk_dual_path_wrapper(name): + original = getattr(_os,name) + def wrap(self,src,dst,*args,**kw): + if self._active: + src,dst = self._remap_pair(name,src,dst,*args,**kw) + return original(src,dst,*args,**kw) + return wrap + + for name in ["rename", "link", "symlink"]: + if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name) + + def _mk_single_path_wrapper(name, original=None): + original = original or getattr(_os,name) + def wrap(self,path,*args,**kw): + if self._active: + path = self._remap_input(name,path,*args,**kw) + return original(path,*args,**kw) + return wrap + + if _file: + _file = _mk_single_path_wrapper('file', _file) + _open = _mk_single_path_wrapper('open', _open) + for name in [ + "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", + "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", + "startfile", "mkfifo", "mknod", "pathconf", "access" + ]: + if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name) + + def _mk_single_with_return(name): + original = getattr(_os,name) + def wrap(self,path,*args,**kw): + if self._active: + path = self._remap_input(name,path,*args,**kw) + return self._remap_output(name, original(path,*args,**kw)) + return original(path,*args,**kw) + return wrap + + for name in ['readlink', 'tempnam']: + if hasattr(_os,name): locals()[name] = _mk_single_with_return(name) + + def _mk_query(name): + original = getattr(_os,name) + def wrap(self,*args,**kw): + retval = original(*args,**kw) + if self._active: + return self._remap_output(name, retval) + return retval + return wrap + + for name in ['getcwd', 'tmpnam']: + if hasattr(_os,name): locals()[name] = _mk_query(name) + + def _validate_path(self,path): + """Called to remap or validate any path, whether input or output""" + return path + + def _remap_input(self,operation,path,*args,**kw): + """Called for path inputs""" + return self._validate_path(path) + + def _remap_output(self,operation,path): + """Called for path outputs""" + return self._validate_path(path) + + def _remap_pair(self,operation,src,dst,*args,**kw): + """Called for path pairs like rename, link, and symlink operations""" + return ( + self._remap_input(operation+'-from',src,*args,**kw), + self._remap_input(operation+'-to',dst,*args,**kw) + ) + + +if hasattr(os, 'devnull'): + _EXCEPTIONS = [os.devnull,] +else: + _EXCEPTIONS = [] + +try: + from win32com.client.gencache import GetGeneratePath + _EXCEPTIONS.append(GetGeneratePath()) + del GetGeneratePath +except ImportError: + # it appears pywin32 is not installed, so no need to exclude. + pass + +class DirectorySandbox(AbstractSandbox): + """Restrict operations to a single subdirectory - pseudo-chroot""" + + write_ops = dict.fromkeys([ + "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", + "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", + ]) + + _exception_patterns = [ + # Allow lib2to3 to attempt to save a pickled grammar object (#121) + '.*lib2to3.*\.pickle$', + ] + "exempt writing to paths that match the pattern" + + def __init__(self, sandbox, exceptions=_EXCEPTIONS): + self._sandbox = os.path.normcase(os.path.realpath(sandbox)) + self._prefix = os.path.join(self._sandbox,'') + self._exceptions = [ + os.path.normcase(os.path.realpath(path)) + for path in exceptions + ] + AbstractSandbox.__init__(self) + + def _violation(self, operation, *args, **kw): + from setuptools.sandbox import SandboxViolation + raise SandboxViolation(operation, args, kw) + + if _file: + def _file(self, path, mode='r', *args, **kw): + if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): + self._violation("file", path, mode, *args, **kw) + return _file(path,mode,*args,**kw) + + def _open(self, path, mode='r', *args, **kw): + if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): + self._violation("open", path, mode, *args, **kw) + return _open(path,mode,*args,**kw) + + def tmpnam(self): + self._violation("tmpnam") + + def _ok(self, path): + active = self._active + try: + self._active = False + realpath = os.path.normcase(os.path.realpath(path)) + return ( + self._exempted(realpath) + or realpath == self._sandbox + or realpath.startswith(self._prefix) + ) + finally: + self._active = active + + def _exempted(self, filepath): + start_matches = ( + filepath.startswith(exception) + for exception in self._exceptions + ) + pattern_matches = ( + re.match(pattern, filepath) + for pattern in self._exception_patterns + ) + candidates = itertools.chain(start_matches, pattern_matches) + return any(candidates) + + def _remap_input(self, operation, path, *args, **kw): + """Called for path inputs""" + if operation in self.write_ops and not self._ok(path): + self._violation(operation, os.path.realpath(path), *args, **kw) + return path + + def _remap_pair(self, operation, src, dst, *args, **kw): + """Called for path pairs like rename, link, and symlink operations""" + if not self._ok(src) or not self._ok(dst): + self._violation(operation, src, dst, *args, **kw) + return (src,dst) + + def open(self, file, flags, mode=0o777, *args, **kw): + """Called for low-level os.open()""" + if flags & WRITE_FLAGS and not self._ok(file): + self._violation("os.open", file, flags, mode, *args, **kw) + return _os.open(file,flags,mode, *args, **kw) + +WRITE_FLAGS = functools.reduce( + operator.or_, [getattr(_os, a, 0) for a in + "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] +) + +class SandboxViolation(DistutilsError): + """A setup script attempted to modify the filesystem outside the sandbox""" + + def __str__(self): + return """SandboxViolation: %s%r %s + +The package setup script has attempted to modify files on your system +that are not within the EasyInstall build area, and has been aborted. + +This package cannot be safely installed by EasyInstall, and may not +support alternate installation locations even if you run its setup +script by hand. Please inform the package's author and the EasyInstall +maintainers to find out if a fix or workaround is available.""" % self.args + + + + + + + + + + + + + + + + + + + + + + + + + + + +# diff --git a/venv/lib/python3.5/site-packages/setuptools/script (dev).tmpl b/venv/lib/python3.5/site-packages/setuptools/script (dev).tmpl new file mode 100644 index 0000000..d58b1bb --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/script (dev).tmpl @@ -0,0 +1,5 @@ +# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').require(%(spec)r) +__file__ = %(dev_path)r +exec(compile(open(__file__).read(), __file__, 'exec')) diff --git a/venv/lib/python3.5/site-packages/setuptools/script.tmpl b/venv/lib/python3.5/site-packages/setuptools/script.tmpl new file mode 100644 index 0000000..ff5efbc --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/script.tmpl @@ -0,0 +1,3 @@ +# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').run_script(%(spec)r, %(script_name)r) diff --git a/venv/lib/python3.5/site-packages/setuptools/site-patch.py b/venv/lib/python3.5/site-packages/setuptools/site-patch.py new file mode 100644 index 0000000..c216801 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/site-patch.py @@ -0,0 +1,76 @@ +def __boot(): + import sys + import os + PYTHONPATH = os.environ.get('PYTHONPATH') + if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH): + PYTHONPATH = [] + else: + PYTHONPATH = PYTHONPATH.split(os.pathsep) + + pic = getattr(sys,'path_importer_cache',{}) + stdpath = sys.path[len(PYTHONPATH):] + mydir = os.path.dirname(__file__) + #print "searching",stdpath,sys.path + + for item in stdpath: + if item==mydir or not item: + continue # skip if current dir. on Windows, or my own directory + importer = pic.get(item) + if importer is not None: + loader = importer.find_module('site') + if loader is not None: + # This should actually reload the current module + loader.load_module('site') + break + else: + try: + import imp # Avoid import loop in Python >= 3.3 + stream, path, descr = imp.find_module('site',[item]) + except ImportError: + continue + if stream is None: + continue + try: + # This should actually reload the current module + imp.load_module('site',stream,path,descr) + finally: + stream.close() + break + else: + raise ImportError("Couldn't find the real 'site' module") + + #print "loaded", __file__ + + known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp + + oldpos = getattr(sys,'__egginsert',0) # save old insertion position + sys.__egginsert = 0 # and reset the current one + + for item in PYTHONPATH: + addsitedir(item) + + sys.__egginsert += oldpos # restore effective old position + + d, nd = makepath(stdpath[0]) + insert_at = None + new_path = [] + + for item in sys.path: + p, np = makepath(item) + + if np==nd and insert_at is None: + # We've hit the first 'system' path entry, so added entries go here + insert_at = len(new_path) + + if np in known_paths or insert_at is None: + new_path.append(item) + else: + # new path after the insert point, back-insert it + new_path.insert(insert_at, item) + insert_at += 1 + + sys.path[:] = new_path + +if __name__=='site': + __boot() + del __boot diff --git a/venv/lib/python3.5/site-packages/setuptools/ssl_support.py b/venv/lib/python3.5/site-packages/setuptools/ssl_support.py new file mode 100644 index 0000000..657197c --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/ssl_support.py @@ -0,0 +1,243 @@ +import os +import socket +import atexit +import re + +from setuptools.extern.six.moves import urllib, http_client, map + +import pkg_resources +from pkg_resources import ResolutionError, ExtractionError + +try: + import ssl +except ImportError: + ssl = None + +__all__ = [ + 'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths', + 'opener_for' +] + +cert_paths = """ +/etc/pki/tls/certs/ca-bundle.crt +/etc/ssl/certs/ca-certificates.crt +/usr/share/ssl/certs/ca-bundle.crt +/usr/local/share/certs/ca-root.crt +/etc/ssl/cert.pem +/System/Library/OpenSSL/certs/cert.pem +/usr/local/share/certs/ca-root-nss.crt +""".strip().split() + + +try: + HTTPSHandler = urllib.request.HTTPSHandler + HTTPSConnection = http_client.HTTPSConnection +except AttributeError: + HTTPSHandler = HTTPSConnection = object + +is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection) + + +try: + from ssl import CertificateError, match_hostname +except ImportError: + try: + from backports.ssl_match_hostname import CertificateError + from backports.ssl_match_hostname import match_hostname + except ImportError: + CertificateError = None + match_hostname = None + +if not CertificateError: + class CertificateError(ValueError): + pass + +if not match_hostname: + def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r'.') + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +class VerifyingHTTPSHandler(HTTPSHandler): + """Simple verifying handler: no auth, subclasses, timeouts, etc.""" + + def __init__(self, ca_bundle): + self.ca_bundle = ca_bundle + HTTPSHandler.__init__(self) + + def https_open(self, req): + return self.do_open( + lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req + ) + + +class VerifyingHTTPSConn(HTTPSConnection): + """Simple verifying connection: no auth, subclasses, timeouts, etc.""" + def __init__(self, host, ca_bundle, **kw): + HTTPSConnection.__init__(self, host, **kw) + self.ca_bundle = ca_bundle + + def connect(self): + sock = socket.create_connection( + (self.host, self.port), getattr(self, 'source_address', None) + ) + + # Handle the socket if a (proxy) tunnel is present + if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None): + self.sock = sock + self._tunnel() + # http://bugs.python.org/issue7776: Python>=3.4.1 and >=2.7.7 + # change self.host to mean the proxy server host when tunneling is + # being used. Adapt, since we are interested in the destination + # host for the match_hostname() comparison. + actual_host = self._tunnel_host + else: + actual_host = self.host + + self.sock = ssl.wrap_socket( + sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle + ) + try: + match_hostname(self.sock.getpeercert(), actual_host) + except CertificateError: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + +def opener_for(ca_bundle=None): + """Get a urlopen() replacement that uses ca_bundle for verification""" + return urllib.request.build_opener( + VerifyingHTTPSHandler(ca_bundle or find_ca_bundle()) + ).open + + +_wincerts = None + +def get_win_certfile(): + global _wincerts + if _wincerts is not None: + return _wincerts.name + + try: + from wincertstore import CertFile + except ImportError: + return None + + class MyCertFile(CertFile): + def __init__(self, stores=(), certs=()): + CertFile.__init__(self) + for store in stores: + self.addstore(store) + self.addcerts(certs) + atexit.register(self.close) + + def close(self): + try: + super(MyCertFile, self).close() + except OSError: + pass + + _wincerts = MyCertFile(stores=['CA', 'ROOT']) + return _wincerts.name + + +def find_ca_bundle(): + """Return an existing CA bundle path, or None""" + if os.name=='nt': + return get_win_certfile() + else: + for cert_path in cert_paths: + if os.path.isfile(cert_path): + return cert_path + try: + return pkg_resources.resource_filename('certifi', 'cacert.pem') + except (ImportError, ResolutionError, ExtractionError): + return None diff --git a/venv/lib/python3.5/site-packages/setuptools/unicode_utils.py b/venv/lib/python3.5/site-packages/setuptools/unicode_utils.py new file mode 100644 index 0000000..ffab3e2 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/unicode_utils.py @@ -0,0 +1,43 @@ +import unicodedata +import sys + +from setuptools.extern import six + +# HFS Plus uses decomposed UTF-8 +def decompose(path): + if isinstance(path, six.text_type): + return unicodedata.normalize('NFD', path) + try: + path = path.decode('utf-8') + path = unicodedata.normalize('NFD', path) + path = path.encode('utf-8') + except UnicodeError: + pass # Not UTF-8 + return path + + +def filesys_decode(path): + """ + Ensure that the given path is decoded, + NONE when no expected encoding works + """ + + if isinstance(path, six.text_type): + return path + + fs_enc = sys.getfilesystemencoding() or 'utf-8' + candidates = fs_enc, 'utf-8' + + for enc in candidates: + try: + return path.decode(enc) + except UnicodeDecodeError: + continue + + +def try_encode(string, enc): + "turn unicode encoding into a functional routine" + try: + return string.encode(enc) + except UnicodeEncodeError: + return None diff --git a/venv/lib/python3.5/site-packages/setuptools/utils.py b/venv/lib/python3.5/site-packages/setuptools/utils.py new file mode 100644 index 0000000..91e4b87 --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/utils.py @@ -0,0 +1,11 @@ +import os +import os.path + + +def cs_path_exists(fspath): + if not os.path.exists(fspath): + return False + # make absolute so we always have a directory + abspath = os.path.abspath(fspath) + directory, filename = os.path.split(abspath) + return filename in os.listdir(directory) \ No newline at end of file diff --git a/venv/lib/python3.5/site-packages/setuptools/version.py b/venv/lib/python3.5/site-packages/setuptools/version.py new file mode 100644 index 0000000..049e7fe --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/version.py @@ -0,0 +1,6 @@ +import pkg_resources + +try: + __version__ = pkg_resources.require('setuptools')[0].version +except Exception: + __version__ = 'unknown' diff --git a/venv/lib/python3.5/site-packages/setuptools/windows_support.py b/venv/lib/python3.5/site-packages/setuptools/windows_support.py new file mode 100644 index 0000000..cb977cf --- /dev/null +++ b/venv/lib/python3.5/site-packages/setuptools/windows_support.py @@ -0,0 +1,29 @@ +import platform +import ctypes + + +def windows_only(func): + if platform.system() != 'Windows': + return lambda *args, **kwargs: None + return func + + +@windows_only +def hide_file(path): + """ + Set the hidden attribute on a file or directory. + + From http://stackoverflow.com/questions/19622133/ + + `path` must be text. + """ + __import__('ctypes.wintypes') + SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW + SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD + SetFileAttributes.restype = ctypes.wintypes.BOOL + + FILE_ATTRIBUTE_HIDDEN = 0x02 + + ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN) + if not ret: + raise ctypes.WinError() diff --git a/venv/lib64 b/venv/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/venv/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/venv/pip-selfcheck.json b/venv/pip-selfcheck.json new file mode 100644 index 0000000..ecfa635 --- /dev/null +++ b/venv/pip-selfcheck.json @@ -0,0 +1 @@ +{"last_check":"2017-07-04T18:09:56Z","pypi_version":"9.0.1"} \ No newline at end of file diff --git a/venv/pyvenv.cfg b/venv/pyvenv.cfg new file mode 100644 index 0000000..20ac621 --- /dev/null +++ b/venv/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.5.2 diff --git a/venv/share/python-wheels/CacheControl-0.11.5-py2.py3-none-any.whl b/venv/share/python-wheels/CacheControl-0.11.5-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..12b34d6eda93e767800f22eb5aa62fa852d6fa6e GIT binary patch literal 17006 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%0As{a%Dj#!dIWf7 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 EA7CnoS^xk5 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/chardet-2.3.0-py2.py3-none-any.whl b/venv/share/python-wheels/chardet-2.3.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..daa9a1241c3b91237c744fcd2fd5f2b71e91177b GIT binary patch literal 180893 zcmaI7RahLs)&+{Ydk9XD1b2cVccm9X- zP%qupU8{Gmy|z@hqAV;N9uyQ55|l#;mvrSY7U~TQ6jU2L6cpXZ(cczEP9~-_M>l(MM>mUDgW zscsvEVRm!+>Vzb`_%>!)GjFe?A&sKG>0vm5SKa)VuWuQwM|}9)0lb6`42}$qJMbZR zhmrWiQznJV$)9`()W4`(9=?MHm%Eo)9d_pVVhXX>hYZ#S-?=k__xe+<_vbQ!uiFcV zM;Fd@)w;&qpL?I@^5oyJvp2%*-kjkG0qb`q9q|%pS2g~puXa!9*_wBFOy8t^TIGyl zP1Uv$BxLRtQK;K%Z1ct!k(LN&)1E-4jbx&&Uc!QxZf!6}i5fnN&aTcOt?kf9GM2rj zX@D0pCfzZEyD#gOj3{zQot^pf#I~)1^c-hWO1N~V4hz!lP-$YbEGX83xXzzU} zFPkjd+j+G+ob$rsoa1Ugbzu8jE%fzRnLE?;f)4XJ@v;SmHNV6jz%yBZmS?1WQMk2J z99mYZshKWEvsH zVo>Yk^|t2@1Q>rTd3}Dt%o+y5T_U7hn$p zsu9Ls7ay7g9RgZc`J@|WROYeU;tocIVQa~TVr1fHxf5R)`K}e*w+d0H^)C@DJ-AoN z^$MWM=D+47M-~BoOusCZmNrTo;cD9a0`LhkhHeJ}Jmh{TAz5PXdDqPuM^|zW+QBs| z{ROuw)3SC?2=U4g8v&xh>k;Pt$_?#9Dq(>OG2QtBcyj>H9 zpHCsQU)>Z@a%yb zd5vYgOfenRWA*m5>MXQgE8`;@#4!NC@uzlSt_u|m&7%)T?z6z&4U=}R!+2L-d8{IP zNRP0&yTCBkE*B74+l}Hh!9nTpJFo+btYrC5gn0nFdgPz4SR}uzIR73CZ>=;XVnL-L z-!C_f%`$k%qym=+Xa)FXL-o>F{Qlnl^Qu3btdUV(qWq!Tr+E^RrMZ|ycBpGYS)5s` zzCQGA+|st`nZfmLia0BhsH7mpEa^GsmtzYJF0GGyBLj0-!)vO!O|GNt)FIx~#{K2{ zNn9+4U5qAj@giUcfkSPdo{5pMr6iCwME2V7$3RnlLL)t{+;*iLTy-#p))}rWlg&l0 z_&N(!>o=5Y?qZp}(GBWI15P%yfgQA1QFPqo^2lS)=fub-J-}Wczimc$* zVp+8rf&{|?b+>V@<_3DSvkQC+jPv!%;)$8JmI9h?t)riEquX!gHh&oUG_g6jH`CB| zmOK=b?J|c`8;-nHa<@)zO*UsSe|wwu(?8b|?9re0?cIj+tOJ-4W{?fVjT|i}Py8AP zMUHNN&soO(SiR~q!MkS;V)pST@g6T@)`eXBlHBxT)?WAS^~Cbn<3DEPQV1#O58rhx z8xh-=I6Ri77oeOqG+kV2%WyYxBn4}}w?`7_5lsUgF{(Ev3Y_~Z;$K_;TL=^c&y1k&li!jq52|GXenoF%i5EcIqIDMQzo8B3Fs8sE!Rp%4REUy843% z&Pn)yC2|aYtE~!$lBR9}{C}_qw@fpU|Jmt0%{9yfPyL|oZf5HzD8r3XE=SYc4 z@27BOsUAx0lDk|XZ;ywj=advnq zm6*B?j5o202oshTZz`&1?ZUJkyx<>F$u^BBpl$2KXdT8?tor7~k**`DW!eDYJJf9c z0nmv4MAWQl!LuMOc$Aa~(-u&NrtjjpYw?~Usd6IUsvoEa8|GmNLM9PC`jXIbZWi`! znuAGPW&V7dI_iJzky;|r{SuRJ-6MHC2bY%2N*5;BVdxPAK^3kFEIwprt<|F6oZXxr(?wHnmnu_W8_2;T6W;{w)2KaxqNX?NZ83zdp zN(Tc9itK-Ak+Ouig7OdF)ZxT`+BhK~TsC_uFf1Y<9UdkG5t$n9GYn=QKl1&(xPh$u zR&EM+)Rl=EffoKjFBaXrzYoA7Y82$}dS)~M8?XcEnogZ)Z5RDnTaTZ*`A;`+AZ4wx zfs%oWT0ksrt3|+F8Iv`?&L6_?0!u*fDHTH+5L|C!J$OZ3i=!;5{d@bTP3&)lMIe5l zXzKR?ZyhlN!OyjbIEHvH8B6H4>1f`L?kMJQQtJtNh`rVaPqcia#broCU*qowUK-g6 z8}~S)gnD?}`oT9@Kjtyg3Ihsl;)l)YBvDMRxqiln-@(Rk(&R{>b9%^a;3U_u3V z??yQiM>*t1HLr+LXK~jYLEAAM2_ zG8X1`*`0!+<|Mt&i#9Yb=iAH5Whw1-iSL`Bl2DY*$;?07_f@YM6G^mpt8OcTOr}|c z`5sYZN*mDTbGg+Pu;@Sz9HB_GF?VkZ>r39iP5lBsOK2&{;qQ+#!Y;T5Jj@QDwp_Dd*D-0*!# z=g*Xt-)hf_cBI2cKOIM>${HA2v_ja7v$mNBvSg~c!pfVZ>ToqO z&EuL+cBPv<(f(lw6e1Fdx+Rgi;>q})ZE5)qjd@Yc?K`%8F2a|DE*QCnXFES~COHeQ zu7*zQ9&PasKz0HGOk%ocy(Xe+oSe?Em$eYWS@ZT^jyrFAn*#HbXyJKBi*C8~hx?bf zc5!Y0x_>DwC(Hhuu(&ZMec}UHKCJpC)@11j%Q8&%D|ZYh^~%Kp6E0Gbe~v`3TY-$$ ztzuO0_|Hg!!0sYZylhs83rY}C{*LT-Sf_}Me*#j7aC9RIolZ|U$OPO7?CQ}ER=3NU zdMxwHJ51>ZZC2)o2TK7}_Yb-J&Op9wf-FqGv`6P@dpVv=f(EW-shR?8Q}iqs7*bhSO-*gSx2( zV1p9jo`PO!?JwmVdY@PN4#T_GH-NK$l1Hd4?u{pzC`>j}Z5JN-ZeA$u2@$ngeaW_? zh44*|S|47k{D~QycME-5d5Ji5dE-&35HdB?Jqv6-88;@;kir1B-N>EaCyTgXa2#(w zZ%W#8X{viKi_mMaqQhpoOLwWrbA}vKtGM?FAcyaJ51J6RxbbaXR@CN~Nk-v9k~`&e zp{kbGS+l<+eX^QrqiO~Cns-3^;O(V(b^R1YXZ#9J*%@HVb|dPkqVSs%lzTLLf06@; z+OtXa;vrLn33PuHYA?@7CAI!l)*f8b`-$fZDMv{H9`fMOF$Z5KGn8~+NSj@hn&wuB zQdWomzsmg4zP7QT0;J6L*d(i3$F3Mo(Va@q@eN*Ar<73>x^G8usV9<`IT7ONDAHse z9o2HifK;K@Sw1{9wI2rcmY)XG-)%TL_-})U_!`|uW!yQ@!{BW<!~bo zJwm_#QA&^1PE_UBAWb0o@$ZC8$~_3MX0MNs zl}w^|cblHfnUvSzd*JSmI($N^Z?{h4Ofj@YK0v;%Lk<;|c(%Z(u#SL_>n{aN)MI=pC z7h{VKNXo0C-k1392a@7~n_;=oJsMf zt6mt3l+qD4eI5X9l1AwIG;dalkmFg`QqR4Jp%6h9V@;k6B@khSWXqD~#3e04u!(mZ z0G0hVKmvL8V};+V)=Q4-DyuA>F-ha-n+(hTi^-8XOYm6PjPDPMbhV}p#m&peBZ#2~ z6u-7lAgyj|zm})1^=<#D%%+pBiEm41?#-|kGZC_llr_S;=dpgPKlH{_<6DzO&tlSY z{Z>Qebz1H0i2Rx4yBM?^(BeFO%glHU6)x1r_+61BgP}@RboN7-EX--m%fBMNRQlNMcB(T|1?VXl$ck$W=Dq+B)Xs54~$f z@ug_NMceW{bXE~aGD54|QkZd5n2aaUyzUt7WVoo8tmzj&f-_ZK-rp1PSzASeD;?)| zT{Bpd?yx=-DB0&p7{Ie86aLA3bA65*orpADW$GCCH&noVIKrP#Og3JE#so3oxBv7W z_M4cWC_t!Y_4jP?g}>&M3EGWuWc#BvQ5k@&^~O(VW^VL8{sd*^B9&eQ6}nD6s?%e0 zCYyD-Z7bBpnS8K5k6iJ4w3%h}d(xXsj85kBi0PCD)3Iw68`ruP6`Sw6c%nlM=-Q(x z8JoHlc3B>jH;OQPjNr}t_J%37&3iB%uA@j++dHesrBr#L8H2PkWFNQV9BdX6r_S_u zS?z}!HF*UTSngM*r6#zBNr-vc^0~^qCy3(Y9VPl~v?KSIr|HxgwI@}IoLG* zaDy+k*`wz=@7a|d-jNj^K=ym>4f%>TmoLK|D5&1V`Zx^3LoWHkd1=QdY<=|$L%$u3 z&&xALM=l8Rdt|}QFGZkbVBNB84fu-Z#nApomu-fQJ5^5Nc0u`;tr#mfMpt>IN2?GI zQwIl(`QMdI)mjj)!ABNh_c2KSM>e7HQ$j)xb^7P8$ysz+rf*Xd)BluN7dW<^6eeYv zWEe+T8kB#^j50DuvLIF{&9KgJG%Rq;?4yiLv&=p)E}_#i$xM#QH7L`6{r2ksMOv;! znZBHJZgPBDcJbFlRY)We^^aDl|3RQ|?D;5}kTtTM-9j{W~!H>BpM5{`H8OaTJaeh0JZ6+X%{Z5_Sj?hT*Lh;yb#C+T?oYH#%nnk7 zk&Yw~C<6#p>bBKr4lgR%wX?xj4JQKLy)hhx>J~O%3Bm&u-501zM2O>tsEQ6ZQ}h?P z_Sj9w)l=9ao}qcB-aq$3BzgK0TUubYHV{a*kYw#*9I}55l~|V-y6CrZeW;}4igAPc z1}hfxI-tFy#Hkgp=8{?>)+B{%+jOqGoIgF;UvBPp zcXap+O8=D&ASSaZU->DYd!D`W0$Syrf|o-^4Hb#k8yPSxX3D(Y^-EA*hdkesz7aBA z4fOlt$A2AfHU1fi?V_d#yxl;^$8qmBxu^Uz#PtdvPO4VyjbhQFC;G65diUbHp69DK zsIIXQ#!h%eZ?L$KCRQ-ka*uL9_=%=}JSxxmi;9Kix3|FR>7_c#U@+!KcMtdfXE2L_fu)_Li-7^NgJ+6LuU(c1R@WVV(GwQ6k`>))iQP9NM7r`g z+fN+{3QI8JD{fuZ7tC(YuNXv$s?x=7@~j+JH;0omXI-SM7*~bgCkmx`8w;K znNSR7!eTt_f==OKalb>Km?5g%DEKi6$2k7uI=P1M47-ygR0uo~}muiE3sfpatF%}ceSq*nQ|g$!6R3$8!6Jja8Mq&c=5 z3YZoGhrUJpK@fDLq!6V?KPG1ib9PWHpS6qY1IWCm^JAR6kuXe zyAc4*x=nAB2EQv^;(yO5Fx5@JJJ!*zr3|DB4kArh%BB)d6hNrpQ-w5%7wpHI=?4jX z=7Y%%R$U5CI&|Sz@7iRp7q|ij|19lB%nMr>*k=)Ch(H!P_^zZ7s=OLp&)#h3i-g4A zN{oR>9tI4i?4d24@P^)bdnAX%MBm-jU-$I;QW9+g-OnEB~0kSs8%Vl-B zW=zJpl@;Pz?IN*lTQQ&SmCg>iEIlqSfLD**C#M84Stet8%Mk&_wtzdlZO#K+gZa^!XS zSiKB(w2*JPjr;rDUDB6<3avDK5w6WACLUOhkGCc!29JSq-w3@YRh*E^1Ral zB7cj&e7ac!kw0Dm-mecIhIzIG-mf*h-}1;GUSNHFK|sHSJRu>!w>yA02~cfgaO>^K zJboGge!QW{1Ha!KWL<6fJb`$&&Uv1nU){-tJHh>!;7sB7_p5gm$l=Y?5s>_SZLh^1 z0B-qsu(zktBB`gFn;Y<6i$D0O40Lq~x~h12ydW3q{8-lPBA$G+)6)m?9#0OLgMED+ zHrRq(KfOG342SbZ2VIfdZ$55$J^}#G*B=ud{oH)KRyX#lq-r1H4g5{sx-z$Budlpj{C$1a zs%~~v#;4y8@!p2B{O&wnr|cjL&RfDR;ow4l%X+W3oyooMtF5=2aC-w+Oz_IEKX|ta za+|f~|M2t%gxo&W=J`J#90A@>4c@zj*Elx(FCNweyTEtuOhH=$FV#YwexRFL!RyQI zJpY@6X^3pg)0?@$+i7&|d+03WHoDdi9K8j;ycyr}d477kS(^rYoXdl}Ykb`H>Ea6T zc46-aik>Em-|F(bvy94^0C0^$rtu*49dCE9xuf=*K0e^bwJT5+M3&_WB-mpQ@O(RY z002%*-(M!zQp<=7z@%rt1d0pU^!LX+*;Z9GGtL(vOi&~s=Oz^CE!vN)#wW<`2bW^k|$;LAPg z^j8CxOZOlBebvyYAr{(>w%^Pb5|ZZJdrh?Td+RTo@_X6aqXWhJgPy|gI9#XG1cyNW z@1HL=UHi$V6#P36@1CR~_wJDEw{wr0u7vj&y|aG6b1Tr_kTCnqU%dD+e-m(K`vR8Y zp3n1tdQpfMZcQ#G(J-0Id2Qx2IOM@3rXP^^(W;{8hjD=%2s4$>UM(Q5E*$ zq}Pppj_cg6N4HmgP`t+{*9kk$p(;l+$O7sHBm*2WoL#H!^?WwVRJ#HpNvVws1`M_9 z$1*%$f2tkQ9j@Y7S_a469T;kY1N~k!r!|rs11mYh~*`B6dA9y8&uUmtL1)^?l z=PNZf_L2zOUTXK&!BeN65wn0tdnsXG62U@;GuyBRC_I;$Ng_{Ey?U)_BlPz$T z8}8M8Z+$-@Kv?@~BKQ<({!r#MGc3@-W46zm0WRan`8#6LDcIyKnV!E*_tLdnk_9p( zxOI5o0}21-6?44tVBsLoR|EFo~#qA8MWY; zuw9a6@QzRGh)n8nId0`{29V{lNuGiGH72*V>Zl3`S_bEK*-1Ut=4nL7{qtgdGgmlP zng@9|oGWPGQh^@@EB(Y=?c>iO=a{b;a;s%GQ_r`pHG{IW_FFk1=2M}k>EpN;aFoPJ zLcCv=6njK)o0j0~(NFzL<>mCO)X%T6N3se`w=+0iW(u2ZUXoTr8133S#g8iyWUU_SCOK#3Zr6ZK zuYyOi0A6seV3p@1e;)W5HsjLNeCD3uvV3zG3=D>DK7WW8v>>p$&U9@(Y01vHd!D)U_iB9eBB2giv-Sh&Jj{{z$h5}Kh-kEd7e+UfYWahlW^ibF z?MQ5@kO=T%aej7o914I0V$5rG?x9}MH8nTw$)-5-qLS!zCt#BVJZ{Q-DcTh{C4}*D zW9hHCJwe_=I(a!ZX}O)auk4!Deg$NyQ7g>WRMg;f0ykiTSK32EBh-ujpi|L$eAm`c zL>gIYVYHSA|D+#4UlTp#H8)4fKE2t`k8Pev^Xhe_;aBXhJ?~o(YPsXN`P#ab_Ut<74gJk~OkKD@q5#piamr$` zgXlY$suc0j^&zHFtk2>B<6Zk!oo}76FDHB@+-0YM387cNcCk!o_0xKfB3M*1WV%~@HEuST(__cf zymcE}sfdtjFd0pRR~`w%P{VNRMwbNq1Q^A?i3p#t8vUA~zmGOx_fkjXE^|K!)S77* zp3OnZ!WeHH_j^ZbqFzE&i8;=_%o{|h{CeC1PE0k8A+#nAB@m?xvkJf;e%vLb)H`dQ(ja1(+Z*{<$iWl8_uO34NJ`)z3as;KBHPrO z<%xn4jdIK0QCZ&B-JEyQw)q5@7ZC{llE+(z@duvxF~>-<6}0*Yv>U)d_;augk_sA( zbO=6}l9v5AgxZ94BPPDsvj$RpMGhps9qgcK{=0?c3{+XPN2Zk&H!yMGiD6?_K%?n%I;E$M zdMGR$T2P)s?-B{)bGwj8VwhKU8EraTct$+RYOAL+>4xim14>YEXmw%EQd?i`=`S~nkJmpdm7s#74V+j-sB(k5xbRerE0?fCX=bcNB(+w9 zL798G;g7QNP0I&Ivhb6s=7r28jLBpDH2u+nH**S&?+Z>B?!4LGV{UDEboiV7L{M-P zgdI)xTwW`X9WBE#Sf;2Bv(e{a2VQR8P@6^_$UK5=}puf98jk;<_BYkOVQU=(zWX z5CnREe%Gv)?aeK>7b|t*G2G2~TH<Oo94F)rBiN?M77m$P$}L}wwO zK*}BzGeG!7ZZa~O?NP`sc<#ngwzr_Aggm8!w_dSEP)0F2D{a^4JQ!hN!cZJ(TAT9W z=kDXHRh=C#OoSa|K!=8sMP}I|i=r)uk(#W z9eP`eWlJ=<{d6_7gh|->rCF#~KmRxFcq{rrUmk-Q;E&i_ zwJIrx2=^}5Rz9!1>d&gu3t`}{^ISQ7o?)7Fx6#|{QaW6JCe`4n*^*GBWbwAZ zx@2|Vajo#WzdAwu#X04w;+_;xVfMt*C(!bR69o3}QR6&%Xs%G$gbjG^>rTD0X4kWU zvBc->T(nuRFkagvE@>kRgOceJ5iTN9+Mj3`do!ED3xs|nHVd!pkv`dldyzs~)TSg% zVwDK?w$ml~d`$4I1;f4c#>y1gse#U*H9u+0FhTEvbmrzil#H>tVQ#Uwqi~Y6~b1tsx{}_k;SqN<;B(ZXdfN zhl_aKF6zPljMOdGa!vj|kECYNuuk~noJCM|)1}h=3;WN8q!^g27pb)7iD#Hhi%>1u zy55YnoWQ}UF1u<)eT<1cWc>542vKoh!x|!HHgRs~X=psN;V$$sNeB(%3ur)r^egPT zTetAS6R%O+o|uX>WmsI3=srAqGKxH0fl(#~e1awu5sEfC=-_&NU5b>gqyy5R)So zeA6*T9{`c^WYnKIXoM`T0TJVS6BkIJ(O@q2>6d+7DA?{|@{%(2)+Y+q0SZZ#&%+pD zb}F3>2cB;K#wZhk+-UtBV@GuD*MLcOH%DBLpUO7LC0zYLu%n{p_J-1f3muCPi*fm=+^@?rVQfh9BZHR3Q*) zCw5zrb0h+Z2`2oH)@j^w*ZJ*{N`Z-bi%w|79u;s3ar>f|oHkyCzuqosu}_6xscXuw zZwUFB+~VkNCL>GP+@(zZi82*%^g*FZ*i7$oYfr+U>fSiw8jngwBLiM(hY2#ce^CsP z>co<5*CBT#ptoiNCpmxHk-lJD#vm&4fu}RpPTB^L`*>*Iz*UcxKOE*~H;3&1H!S{L zgWIY8ZO=DUpw(Bx%(mhNzr1ZhA#f;tE;NG~K3k>LInrfU1Wpw1k6R#nsvFNI#$Rlg z%?=xsLXX@&lQxS6Br0W647k$2dh_Z)9v56lqM<`-6^QnNB3Kk3+V1`!&f?T~1u-Pl zK)Vad3--XqqW`7ga3JGnncMhhSE+TNad`U$qbDOUEjuiwcn;$qe7N`LY{||wXq}mo z?(m-P?tnnwtWIe8)%HdjG-%Aw6s-v>%#?f5Z_1x5;;4zfixSfnyy0wb3Z&p+fD0=X zHdy3s)NXrA9lEvW_P46o?0KfiJoZwEaxJ2vis0(PqJ|*(YWx3 zHlg{q-8poL*U^shdwd%==CivaEw?|D7NFR;7%o8&`GQ<|GdSg&0VEitm#oE$`51cF~0#`i^JleW-_yD()#GzI!F7&#S_+uB`h2^o`T3|6AbG+`qy=Gm?H=p~sgd2Cfhy2bEv6EZd`6OP~L?g{xR5 z>oTJ0cMsoYgJJeA=hG8#;Inv4eyL#mvH~7Ta%G^y9H|zf{)t{*g~6lP-9#kTDQ=-m z(mxVN51psfq-8f{J#GJII73GClP0vR>{b{)3)pmXnp2>CB-{K3jwCvGVyD+*&CKHD z@*^f^BnM2CRs8COI%Qe`6@(P#j1K392arU=t&*Rb=#KhfJ8{9qOz&M1>QjB9t7w8V z`q9&M{NhiFc59Bwrr zHl0atU>VcdeEin{ZrJHBmC)z%4udmUMWkZ)uny21gLul#bNDg-AK%PvY(zLm;TS6$ z3}H^mR*rgbxCLOJqG{!ZnAVzZ+~EhxxKe{eA54(6?kIPRhgDthCY%o(h5{?Q9Lm-s z)#M(9<`k^au*r`kKXQvZ*btuP8yT}v{Cl?Zm3c-?7Z_8)sWmb>KjgNyJO1=UM6jD?lWf>cSFsB?ZtF(^o zOP-yiaPpb0o~+RHT*-3GADWrEY(EX zhT{-Mkib&UfoJ&n(g9i7>l)cZvp%5+_57~?+JyNss0S0Ai}uosAZ(nrY#78khN}Su z?hrZ&uL39HzRqQde|GKN^M%>mOujZYae&3H7u|L4u_jpC|1#}OT$CXjvDU43jkEjr zX5nsG!WvOQsw=+-FWz#n04|SYQzayF-gJ_H&#m6%&hzyn^Mi91TFADF5;w=EMQzuf zIqx@Ff#>@DN#Vxo5_*@qI4IWo$&##`MbyP>nh1$@ykCq`TE#39lT*$rC3SWx2N9`9 zWhwBS#eFYzEBYld#9oKc<@kgneORAqIJ&-M1n%O7LWHQaDcn+s@!gX2>r%{WDWXLG zMcPCeYR}#bint)Y)7-dG{bNWKFID*WHp`s!soTKWK2;H_(l6=%@n-sHcq+XzeR)f_ z1Y((@pCTkCp{W-B86b9zze2wnW4}-6bvS^mH_j|w8D*&Mdx%O1l7BXh{Td4^kR^F8 zU~Xr0UMn0QP_gB9*Zq0pGEBH@h6Pyc-2L^|Ey}bdZgKNxJAd675K-ZFgm4zT6&VqK zROT6w2KBui`(02pIne|My6kr%6K~oS+R)j|BXy`ra%qWEWKljWD>q_UWAQ6~7!hmz z;>bq4rS`QY4W^aQ(@|}A=V_C|0j5|_!!KWj6BE-}lc7CZKl{W4&!3u9u z8lyke8KV@!MS$}u4pV7*Hxd~_F`E;K-iuj@=pL}ZUy%>#I@NcyCL68NBJGjSQQe!E z@9bc(pv1AXDrmDeXh)nQJVyz|uqCMdPd|Wqky`;&mQ&Qv#l+w912^foPdc-PMF8Y; zYL{@%rDQ->qSsrfT++-%4KP7!HAIAne>R}{>pogG@7bmVDsQ&mpmE%;O?tP`VnJQZx zt3cgET)7OfC42oWQ2BE}jA>A)g{d-qtNhvMb>^3ed)W;JQxcB-T5%;Q98&!jsxH8XPPLxN$fC79*yeIW+y4v0+G@e8Ue5(kT)h z#HgN6@w38h&Q=2P$rl+HhVm|I%AtxTd`HjD7-q&!6{`mi^e(qNtJ;R|H5u>d^LUKn z6&bMWj*+;SAwju6|A=xj~(pDmq~r$vyIz3Yz~vBz*JefQROeo z`|anhGvMs3{q4dFU}PXiq(beB6(ASIOqY@@djv&i+0*3xbRKxXpM^e~K_zYP!`3WV zdf6mML)atfHu_p{Jq6}BPAuZ-G0V+;#9$NQqwlVav0xBxZ8 zqh#$OB{~jOfffXDwIh?L%(xNL!(JXPFwvy1&XGX=)}GA7OC&=sZ-pFAhc6#*!I<2H z(&+S2yp4)B9M|D&vjPu|u=)cwa_Xv z8|vrd!kKwGJGx6nJk=-fU$2sev%!-f3pORU7-ma0A%r&$K>6Nlazu$MR1LnV<%MrC zV4vJG3=d#TN*xxeZmiR_Fo5U#3BoVVJ#;s)ZtNnm4;2^La7##34AR!PahUxn|!oMRTqEDq* zlrqu0IV>g5?J@`kal`85_)$O+ucr{9GX7V7nl+R_t0@AFie4$ReL zqAkzT`fE@-aQ^96^7HxuCnNtWy1lhhxPTybgUfg5({SHy^o;WqU+nqV1dzv#Y?!sz zQ|ST?T9`Rp0X9sszeuO@WrY$m)CQ-(f;IORtMoC3j zN@qIn--2kfW1^i7yGFH?z*b;vJFp2hWn78M_=j?F+%Q&Zr3kkFW}qs=Gw1YZFIOpa zpP`|by$0eq{Yc>NHO0el+YGYVWe;p+W80DC1_S4_sNF$As)JW{A}d=(XL7#5 z;X^7*ADKx%g=7DoTq|Ok)ah>OhpNXLJzm{X(CY7>Jf{wJvQ?GYkEsa8e<$sRJTYD) zv+LPG^sYw5nn)8J?ZDx%6&>X2KUdeGU-)fUnr6oqRKRur)+G-@#(C)SFI}k)P^Hcq zvu9FU6*&YFZtsa;aH8tT%{=qVgKeXj$aMt5R}Ei%KO&gI9miNLpU)GnD4lnxXr4GJ z$2od{h+jmjcLIUl;mH`vdO=x0^xJLo=xBxZGhD|YMCkSVe5sagKZh~knzszIjnSc3!tjG9=+>0Fv7W_O9a-y$*_lM;c$E|mgrb<;{p zE}M_(U`wX=l60?ov1iIvT9?mDpAwgHbGcDhHY<9xw=u=;XC5y!bU-pGU^b>!E>Vj} zsCNuHEP_^ZFQm(pI5F?KM(YtEHg&YdP&1b|xeBN~Oee_@(UI7rFaDr+jLEn)@rRRT z%)Xgf+LiLX69a-~&iB+~{$`m!^!`-878Iin zP?5$9@p!ECV18(O9c1Y>*D~CpdGz1r2>s&=^wSZJe8rAipUEDXPjOQy;AYb4XgjLA zY+@8t{DSTx?K+MjKqj5Pq^Y$l@g-AjG%+dU;8xBv6HJhs!d;idPWLg4uaZ zdH1fKV*$;dKjpsRJC$n3K}@FX9~%`0aOxyvD1C}R45P?jXjqr7MVAlj8Hm2W?uJ#9*ha07p>eR!|7oK91GEzDv*r=(r`|OIC)+jjj|T+M>2H?Rgl=(6wGp9Mp(6G6lfC^CoNZ6s5&}IA za~^$Bg4=--K5y4(;rRdf*?Y0m3prd7Bxi5kS*I#)3lRK;1mnzOTMvj{yQ4%m&J3H$ zR7bMIDZP+g1h6A0zn8q6U!XGB>?DSxA)_um#;shXahjfe1Wg6YOYZY9g0vmvCl-8_ zRJ}@;yMm(M8vAUcI1*F-9yR@qIAACFWfUC+TCvWl{;?h>8+rEGVM}V`FY(?AXa6)8 zZ+{Smeo_pqzpMX12;0;A7x|xn0+u}1t`4>|Kq({D zUC>tWh-*%4%vW+SP>*htt=a8*0J6g^nU^N@xVuD#tl>FD`IRp>I;IyX$w+={sorDG zk#RJeumkd5KAKT@kJJ5gIBuMt$rVuoZ==j?l4Nba@)SgbQncMaJ!|T=9vv?Bw`(Xs z34lWRF^004m2jc=9#InVEGqy<-Q|||B1Nntt9mxeBuD=NSmSF$z!Ze4qLXkNGC$?3 zA~$2Sxp1q!%rs*{E)V}tPQoXTE?B(3h*cqc{5RznY`Etyb9|^Tn@oBzf=VH=^f40y z2~(6cloOEFX=zraE|wU_uXY}p>m2y3_+I995^2lk+dCMkzBsRcEzJzQn8!YcAtgV0 zCVUv2HA1UYxKW+2x1Z(_ED(7ppy&x8p{PL8l}EbEtaNb!uOD@UyVmy4v!?t^eIFKN-7|>)!8ux)sbDe%giDB$|_R#-_$x+n%S2XGV5~r9|?1*_I}d&exJ|IMAu%2@#IHm!?f7C9S}!8kI*M2}KscF|u0! zQ=hTXx`aenNF^8pIN96NlILb37s?ah`te1M9o)RnK&mXWJ8@_|25&DJY1bo|^}_|E zmjwhiWpeuFd`qJqcLoeThbh`2Viw`uIG%`C<^8q2rT;|m)!sj+9mw^o(VKCPHDfnH zZrwcTBn|X+`&g@V4Ku*Fn19iW{H#J5GgP%e zd*SSdSoA)>2x+c*Yj6LhU;nEtZ{CAyJ1cve5>}$m3#0sH!IxA5AiCrwFhroFB6!)$ zi`~iAFYLni`(IImO*V>6&ITb}o}<7VsT=$mW4rf2gWK!ko|?o*^m^7;ftR{Ve31i3Nef#*O6g{MwJuJcmNAXuth{9uEJMa&(VkH3zBV9sSE?mOG z`uy=#wy(c&X%}yp#i(^&X#*Ze;LmWcsQ&y%0x3S8tFrxN)zb2tY?$T0FQZk0WU61i zEWGYgxdkT{p&Iu#D#}~(_3*J zwqNKH>U5jZ31Z$M2~9WY!$9B_i^iJDp9>P|-#KhuibV8cgv^qxgOx$;yy@4hjr7ql zfmm>#x+L4%YB;D;oA1lE*_JQ*d6#gUnckO@7P^?ngX$B)>GF_}!&9)-3($bU$8je6 zoWFLOjL6Qj2@v{2V)5^m{?iUnDKVD5siQFijs?wcL`zA8#p}KeSdi))89J8Eo1>6F zxRLPCExXp{LL{8+b`^Z&NE}LqXyuRRqleJU!aaSgjiQI^)XXak6wJ@rs&Vjqcm2h#P_epYhNAqVb2nt7k z)OS2Ap7#4q+f0<6u~5WnU(W0CzF_0kEXFo+$bX+_M~$5v>V+m~JiHgWZ}*m!=7GBZ z0~%M$DiBfm>f?q7=7f7DPh+AMCB*~XZFk5;gGgmLx%VoT3EQoAhAiBUPDXyx$GsWp? z?6#2se2pz{#bfc|-S1Q}^tu2WTdNFt#HRF^sZlNhF1&e-cfqBdEoQHL@8X<(pc#mx z%YULmln=jJEAJ+w_zp`X%>+krOe;Ut{LV7_GE-E2(fq5=nI2vd&(bsKxX|1kY9FL< zG@d8Q)8&ib34Ki;T1O_@!V;;c$u085Ky7$l<)&+djJd%?pm=_H4q+!(jACe2o+7HO zxJ74yR#0fPn~E=a$ZhCn8hYc?%a(AhCz6h5AMhoMSw=H(jHKQ-r&RqGwKRE6?Jj4c zcj}he64{)7`?_ZcgB;E@uXXg!tQf)hI2$cyhADU{e|1jUqTvNLBXrG+^7m@~$2AX8aD5Qd%M` z{{_)`GuAhTXVopVomL7+zl9MTNZiAehBW|>`7MUgk-p<^^i;nLF}t~44vok&bg$~P z?+ChW8H%{&`DJH_oY>r>yjZlnS;eXoe8ydO$NOw&zHA4Lc>N)raw;BJa~IXs&kO;?HVP3r!j2 zP2Lk@@j8E9O44PM8iiZe+hr%Bx?MgUCDXS0ReT`=Uil`qnqxvrFX0YrTOpo$Zp~z; zbi*$Vj)2J)nxX|S!H*vI>fE2axpAMd++d^lwO2wfGm( zoMrP6`7VUiTtf8JWbuiu={VK$9CbT-Rbc`bB`kh54#@ckhuk!H!gu-|6lmkRmNP}ms>J4_-MsMkt48-K zzBI_}e_;T&>h+VP%^q@%>3n3Fx0HTP-fAkZCsiP0-F|fNQ(@en=_g&#R9hTu$wMze zB}E>dWP*P5ZT9oPl{L=9KoH0J`|Ya9Yay<8RWp3siaND1yE+CFw;59ionK?xk}KN_ z_8G3esEt**J{iB0%+12#srkg{<5ZHyYP|H-_LtGDW^O9gvwh`$k)iXG%byqmmjY`8 zzl0m;jZ`O9S#uGAr%6788-|yfWGi%r(){uBR==0tC8d6PcjJ-EEIp30F7et#0%N*n zPr=&2#`__rW&5!x#HERp)YQOxo3UO}nu1wn7oAqETfE$Va3v*RhN4^yGhK#Sx0`zA zJ)SOqei!k`wa>;Rj;$wIU+1x92(P#wm`|V~;2U3UyJ-`H3NEnXejVUzj?1?A#Cu-t z70Z4GD(EcISXKeY81&&uju%Mq`GQ6k_H56+ZSPVt)Fo&lanAN}hZ}g7oI)l;tV{JB z3Fm{*dW-IQXm?D}$S3rf*41hqKA0QKg5s+)?Jc&bJ0FPP7bkA>sVhL1$X0JYvN5cF zuD27<)rXW&wSQ-*_FL4*w;GP0mzFVk4_??#~-P z-pw6MH))cOhjveDgWBn z!6_4q`2Bm(8d#!IHHFRV zLz#v7N=3Rwgkwj_7wZxGZyRi%);#>u_?9|lGP(6*q9+l^)*XrbwJldh0=ba5drz7)lU)nFGOZ@EkE_e z+d%B4_FZ)rRS1^dqF8PB}MOu{0qv zQogrK30LsqH(M39iW#Eh65~;`I?=R`y?b4_`8WX~)3dFqnkwB>o9U8oZ6R!a^BVL_ z1TZedDl|g6VR1@#q}*dh765Jm#9A$Z-kF5jf+)puIaC1eQ! zGNY(B2e~wrt@t|1wQ=S)$Eto-p^yD1x2tuMycdPMuQ;QoOO|Bl_+o4u?8{4ETJy5 zyuQfp&SyG~)G{CESX>&Aj~Uueb;^x8H~h#BL05Cp(s_2w70EX6$bGzh(lluYoHZWf z{48IuwsL1*xMRL~+4#D;2u(uEI=|#@xW%hc4+%S%F5As-furNM6X=Us=-g;*f(Li~ z1cIF$1V*U*Ayc2Kmp?GEfcb;L_2;gJKIms?@PIe+QJWK@2kFBPK+mt`T)ZH}D zDDUMibC6d{wD`40Yz_DK z`mh_v?Lx(uix%e+Vvt2uio^4uCgwV39$3ut7vEYxCHnRl#6Hy;it}$_+m3a$bbj?^ z?LPBwK6Kq3RvP-nEAxI17lNlmzL>7zKVa*={j!;?@{=O4E2`EK+gg1{V#o=6n=-$A zF;zVNQ$+^GU%mC^Xt%Aa9WM!LZIa~)?LCj)oi8^0)Szc&ZI6M{Vq4TOq*b>%)@^{! zcqnhnq#q)HvVy}{Mld(M-}c?lXkLZy>?46wvrmh>=oh_O?x3aR=W?rvVkv*2NH^D% zq-&VkB->vBrb1uI{bUHg@kz zy|Nn3_ISCsxqwTj&~{%(1^rfoE!3eYdTl*-K~>=$HG(b--1Kv|zzWXav*db&J* z>5NjR!%uI68}1sK+g#Tj#78!PV`>M6TXRono5E7rqc#zq^0D;Gk&3xWOmfr|O7;Uv z#nW`<#-f{EuE1)|(X*Df9Fd`4+FtqDz&ts0Y)rzy*Zm1YdAP0Bmsu~!h@uaM4B|Fd z;i~enVNaKh4u|%ku#`@R-kXpE<1n(eh#KFqK$A5|>k1ff?gIQigLjF>HYG%j?*Jrw znacmxwBMM4nC0c6giSkBn!o#y(S9YWwG}gxDj6aY?h25*+%CcAt$@~6gKgC2y`o$pXn`b0KR6TmCKQ} z@x4uTF;F<@S6N?=hAUw>#+8QF-LAQt*jU`%W1?^LLk(9kgxGm(e8chVybiD7r|28X zvWAySHU|aEX%T()HQkXHTRisptK?=&cP8~kU*j;hgzG*m`CQXMRp;C;WDmxncbcGL zvHfBSy$l=3H#B#9-&q~<-O=V;190OC zI?U@eW&l%Bs#2(U*4&pqa`%kynA^+MWNsBMOPuvjUHw|2B35h#ectq1JsYlPU|F7B z9-E!}v&J51oDE~d!pq5#-{_UE%~5OJi0NQ8$$d|20vdYE2DK=lgqJ~U`aCA`suA0R8OP-lVh-Km)K4ShgP*LG?HB{ z@deRW6N86|c090#xaJIDQxrt^QE;#`s@~bgfpVu$eFhp;|E@d53aNz?y;-Mq!)?_H#a4jE-Q75)yAo;CJ?v%l&C7Kk|JwNf%hDE z{Z?at70mzGF0g-@*CoVzCz1F}g?_JcO%faSWU7XG6!86E|SG#zV+(_ll)>K{GK8u0HPHWt2 zo61d(>s34BbuLR)K6O#!;6`mbx+k+V){m^oZZsBrR^{1zylt9DOR&V7R%;$7fcFo6u@r^ko%(DIt&(iLyTNgv zFL30Az9^=&wR_o`JYj6--#=j)SKmtBJpWMR-UsWSsw)cp80}_}{cN2rvKKFz=B&h1=%=%{j6O{>oXKv^ z%^sufo*=26xYf0)M8>h0h0IQp$lkd|2;Kju?qyNQgmFm*u<=3MH}$Q2oAQlW=|7QUg6Dd@%$?jUc#YOM zhxFLE3$p9vyBc3;JfdOXtuWNnEgwu%4E8VX@Fr`w(%YfFIEzLzJb+^O-iBfrJw7Bq z49XfH1U;k=3&hVnyQd5M zEJX(O?b7#px2pQ_XV;h(lLI^+-1)N6H<3Ji>N*JpG8S%KtErPJ&2J+8%_wxpkJ|$06$u-Rw7Dd)Y1~s^ei$Y-+A#lnIa?zYz3DC;1g>}A~KnpFQ=cY zQ|F+?&0`8~>2UMCiBxG$HSH3zh>^Ksal7E64n5zCvrf4WKUrUEPB!h#H-Au2Ztm8( znml=}ImAq_pxE55b0V2g$X;g2>B%&ArVClIDNX=9Hb04t_hJm`3nw^jWKue(d>xB408m@&~M+Uw=R!XdI?puma&jOCY zN&f0JE3^Ho2^6(h@gP@RFdORF>wMX(cWLpzwoQ+bqU0Jp=- z!0V*RNU$8Je7D+(s{BW_Ke=aRbrYFPcJ(6=i*L0~gPn94;3cyO|7;tLA*T2}idQBC z_4?2k5{&@_P;&$cdH4@#n3YBrb3_&q!DTe$_BVEHK;2Y^JNi`H!Ie3dEAd3m7-ss? z6OM5Qjrmi{klgUk6SNhZC`LP#X^WdYBeV5?5)Jow;;*Y|yDubR#`23FxFVvF*xF>Q zl8`VA8goZuff^H4I`O%jfX2wyPv7>YsDw0=E-kZe6is&(G1nu%T(+TybdfLcZ2X_F z;N4}4*ls9!b8Cxt7y`Ui0*XLWg-=0=E-B9u^{NxNND;(yNMtjypeXYkZ0RL3Stuy` zzLr3w>&#}oX&95|k_bc^$B2W+!8MFyZAp)%FTSFjgZN9;fHXPJ>m<| zwG^nJxdu7&-4Vj~OJF-@je@bQecoPNi+7fiIAExqPkh-Hbz<7iHOq?*0Uy`syuea- zWe8laR!8Gfk;rV1$t8?s8dV44b<^d=GVXMMyCRYt&u2!HeA zCApjPVgSi^Hh8#Sk_=dRk$83(Y{8y7=^yWbxJ(s32CbD=c4+8}(!h${NOfrfOmWV{ z(BIP%(L%2I5*jO@yh+$p>03dxoh*=B2g(OK&^gSrdPYWp`RGy~!hhYY*2Ldsm7n%y zCzcR5EWq`kFt?Cb!tW3ZHp2I0S}Sh|F~GY{uKf_F8E^eeQm$_-f&6887_Ka-hCFp6 z5%kFdKP#k5sA!}H2%|9OSD}O(ziOC1jmc=+4qdreOip?Q-t{%98p8YSa^UMmTk?w% zMD&=b-3dV0Aj<7ZUsIwVSx`bljpwwf1^xuL5t^XY3s8QrZzGiD9Lq~kqely}0Lz+& zwXGtcL5IJJt#u{+po3W9*E6jVkn*N$G7zOJ0cg%QkMLe6 zRL@qUmnp)B;4K4mLPW+(BL_?h`gW!Ru01$fW99~WMNGi7os@L-Vh0uJy1&$JQjVNq z0(8<-W1g-1Gx3>usFjPxd)Drl`hk5)IaN%^E$Alxi!%c0z9|Ga9AgB~TK-Hl1u3il z??T`enwk7$FFwIr+_D#!#<1GaUD^VxvRH7|j*O??Qa_cT`VlAYRZxmNlssFJ?22;1iw>_@II2OA0_OXT* zQ^Z{&uD8%LrrBz*&0iVb-evTDds9+enSo0UDyx)TJ`H?J&*F@b1Npil1S#!tad9ZC z-Xz8AG(6lVnB4q6y_e|d%lmQ9lpLEHLf0nsG`JZeN(rA?L0_sOa>&hnH2BWaKO;;t zgyL#fL;X2(lQg-F2gQfpMkQ@t&dehHo(UJqN(#N4nM8Ue6U>LJLzuW#8DkJLiiNXf~hRP-fVWfu2#<(5L$~?>`q4WHoB5 zrTk@Odz{)lARd|zcaKc6<+`W~%F^V_CMF@e9*^CB0jhz| z$&6_3NL-&H^IFx|MDIOb;RSOv#uLY^feqN~5A44q6zqSF_abJx>a6gE&ZU+FRtnFi z1hH0)zg%|>gN6@6?WL6!h?xe#49vgY`qSGXW+?27pbBEjmCvWu$Dh(TSH8)0Jr99C z7H!NX+U^GXF{i5f>zX5oD1?%rL?VqYM4&cs3RCJOe{xfVcc^r5;Q6jSPJa_7{{mwK zKDm$&R0^VegP`dP_{P~^jc`wG1Qi*r0krgrvQ@p_t(RD0?+dxLs>*p@(B#XFw+Wk~ z!5`>N-ojIFBSc77uZ4LtN(u)G1HYZ!;xaM7>t;34hLfuzl1aj^K<&Ada|k3Oz^t^X zitybVh;9;1Cg|n!_UzB=X=c3zBT_}+EY}f85_?)GjI&XNKqUygM1A25JXHa4mqZf; zz09G^p@eS!xC|{d2nY&=A~;D*aKA|>CB>z?o%@{NIj;*L5|l$_(NjSp*k%Fea)b$yXxW~P^Z1G;D4!|q;rFWgO! zb`i5Kh77Y5hK#LhIF# zIr^sI^RgPwUaVTMKTu*PIr~(~5QQ5@+2>fDiqFj|Fq_#5TIK^D)+G~T&71XA14RBa zo{oQnYN!nUPIxWL(qJmsrgM)|4`*)Kon=quqLci4{F|&AbH19y5mHIrxM`G!C@EGK z`*WkofDV6gCuFx)Kr-?_aI+{ioP4u>x{v&6^ZZ4&(Q}qXYeb(hUUoGNtsT|~P+Q-(5R3G#reCs;)IPXv|`0A?bIW0T+ z=Hh#@j?EWmN5^|dzuKJMQYRpN$lN>iMuL18GC|NxsqI{ykN=!iV8Pdjm|WVh54Vnz z(YZSB7}Zd?eZg7*sEK;~ghGVPr-%MUjhUq1A_NxT|76KFzD9Fi-WezVr;uPp2wLD^ z*rxfeLLpriQe)9?DK=@5z=}s%8ae>#f+3vY1$3!M@FIc5^j?}O<6aanW-bjK1P#DC zoZ(;S3J%BqMc%%K{W29akT4S+V8i1nJw6B;g>5<47SZJ(6H*l!xNM~p#n!I!w4#d< z6a57CJP2vOAhT%)&K@`Gt1tdZg;zq?h?GYI7XpkDo?PCK6;X}l zI^q`K$df9~F$|i8DZ9AKyz{I|cH5?F4apd=>C862FH*MW^umm82swr(aN#MIt{DcQ zVYV*tvOlP3>6#(X6imQn#Nb6RjgQLdv44^qq6r+-71jcrruE`Of#-f`IHO%^%ju?& z0!smIJT1~jBcL_dF)9xo;KY+Hy;L`?y@+Pm_B%E1UOEdGlIRLMw zBS<)fYA<6Xy|WvB3IkmImuOx3-6&`eHshjDu~xo8S^xRyLlxa`WXDo~H_zYk6I8c! zyI#R-Tr{w&0WLd1|2O4eZn~@3pDF%LJ9hie5sRKFNZ~X1rs)tOnn*MRPA~KZ$umjdP72`WZUfz(AfY*JnB%a+ zhLEIq;@9xX3n!!QazuB-PW$rET3Ab#Qw16-l^GOJoY0;Q;KS6=cB#e6>Cmvo*!i!V zI8GyiJ#Hf_S_}`aC+!t(8sj#v=yN;$dz@Fdn%^7U+cg@QeYd&8^PMAc|E(P2+~27o zMYZ0}|0{Pg=w1)uRPxvvDO5OE%LO+rc)zTPC%ynW*77996^iL32wy+~k9g#iaha}0sT+jg2W1Y#hErAMKZ?(_xNYIKTaoFeb0>dMo< zW#?qxKd9rdm})D8Y&2*%S1Hr}eol-o2L6G;wQwA46*)A= z)IZ9cfD62Yt_5j`*0saxGlZP5_VPZiZ+}%Ahmrh|RJY8WUJ)?3(qXAZoK*ckE5hHZ zKVlI6;@tvJ0*}{(!zBb*CjPaA@Q6r1Vz;i>ny2{`HVjc9_yNmwRw(4r=003?%$@P_=@3ZY1p*TuurzKzsIjfj?(EbjK=JB6 z&^Oq!6FisB6FDxu#rTUzAGtgipv6-zHU0yX+ZLzwh^@@v7cmw~aE~Wb3i<<-1=DbX zXVKk7Ce2BD;n}C`6W^Nlniaeb@bkv$2F1bp9Bb3*c#(#)1gbnrQXE~NV3@L_LNXmK z5-}5s#57#QhT7pk7%a$9Ar^{>(3i%$p6sqEK-PESD`w1dt_b zoc||bO(*CT%+?W}B8=g>7+o7>q)i+L%k3cG=Ju6B)yz;V9;BW(A-##zm|M4$QRLQe znrf>u!tp34Is0$35Zp79s%pGDa4w$!YpkitR7(6lU{jNXwPA$RyKYbt495vClTIFq zGe@Ao<12;ReWVNUt5$E&Kh-ct-@Q|vPKVV8WUmHXmQE*t#H7Kr%cBl2Jy!Y9KGd@T z%2<ctqBYoYKpJ6(T-S1uJ5s>oh1ckP(s^0U$Tg5-};-T{blz1)x(?4*-rVA(X zk33xEoankxIJ*cs<&&HkFPW3HVOC5RK^oZP<9S z)!-c)H>pikJ($Z^^T_&Xuo*}FvPy!7J$nh>5mO{*4;^sglf%G~kUES9Wk>xYuN1T> z5XC1dRL>9`n;-yIn37K%00TSfS2kLWW_qX@9%=cPBoqdq-qDf#QQWV?%_m0WEO1^O z?+e%vI|`UZ_KewK0&pUeRZeoH+7OXqs;+d&w>4|c3c zB-__*Z>9P45JDKpFX348j*L>zb_TATcg{^;jfrBZMgVHkgqBQ%waQx7qNr@%m6ishWM(+vG|e|TxxNwjZWaOP1*LlPA% zDTSGoSI=-3PnAKUw7Qe<6~C>c?2?3UX)l1$F0Jk&jO5RA^h+b-)l+9LHZO4;OHHN39hFyej_@f<?i!*nUVWO@bAb{tqxFO(S+Z{)LCWf^B0|<8S}*@G)}pMY5c&4_owDM#a>_*uENlr z(&lA>vpJdepA-L)gQ;9H8*O_-G9Ft4yrjd2C$KGf!>p2i$p6FAaI>LyIf z(VH-jYNW_4mH$DQ#n#+urT-ENoTzcSI@Nq2)7M*`gKIIAmyZ1)%;i5q$!7Rh?gl>I zkzpbNBnMY3%ugfV1Bxvd}}B5?w-ZU+Yx2mR z>j^BtjTp$w4EqS{a>}gFg#h>TDwb!poon*{4gwSWxP+rZV-k@N6{%PuTx9T45PL?D z_PFZ87xI5AqLy(s%nxhysm%|9M8l9|{DPcWu6L&AV|VCQ@v)viR0Z`w!5Zt`oSl5j zP$(v37w42;k3-~G=i_*CTC)tTQT-n=b~@~GoaR}9e7&oeG>3PO;V4e|HJtpi{y)@5 zJ%#|#4Dfx$e~$WPsp7x8On8gIu(YS`&AY8K0|Fv>;8f-$P55z~aDg|}P$~RQ6j9Ux zas!!DxMni5i)MI_3%redO}AUw#Nbrs9F>N{WPLU@Qe**k%8yPMxkd|3rOX)JiESGq?c47QH`__Qyv+#k|Qw5~kt>zW=+D zoTzme*gXArC}5r4HG()d<2W2u`3_*{NPARh{{+O&99^8pMz9xHW_fP4>wLB8WDY*m z!3DT+IxdjMI!O;Vu5t9HDJV1m)efdAuIHQN-CnoxExGTcVC5wGHInN2xsKje}+efBzPvMgJ&G$TM zBm|dDJA8BX#B;BU1FD3lYkYV<0qK7Dek4Nm8uXHFKZaDw^8$&!Y-s+IhPBK|APi6Q zKV1g?WJbn(B>{EeuI9fp^dF1yo5Ov={J@Hgdol0@;qi{*a9^dbpJN6(-Wsv!9q&@m z5JH8G%3ISUe|Hc6Tz}OPSBa|~ant7qrUNx9Wc9mgj@Hn_;y`v(FQTsZTj3>PG3jIi zdc8m~al+oM$?HYYXYB@0kL_ygS=TXF|V3*$SIP)$mn&#;7V|3?^=&^cZELnEE%I0 zZVJS3oiMc0x06BOH{NxF>KQT;FWh$!@H$~YrEdoV^>26US3x%yy&3q9ntvp)=S(}B zy`|eB<7&8-C_)xX^Rss`3V5d7FAb8nP8eBP-Nm5&8&B21aF&eM>jc@^i+axo{Al3t zF=cnDSJ>YS>@UTlS^LAd23qq*Q;KPN!y5=qWRvp`8SoXgA%XQ{iQRbM)ju@Mf*=c>0kQ86ae}H!=S}DF0u+}QQ zH}pj3dWNYZ0Aw8p6i>j_j6H{eQ($^0e-4)F^HWT8@g0gy>M!v0x8?~b2!N^1907OO zhiOmXef`1fHbd7l&N%8o6Jclz;JsKKN8liSvz_Tx$54R}Y-hT_r|s$MKFLE;S%CDA z{s{bV(2>o+0y7CZpY1UHk6|&>Wfd9i4^vPsE3>nPTOXeiBJX!t0HEDsBYu2DK<7Ma zPV!>{07j$KoTqlr6?I}q4@r)|Y>2UrY+~hd+G*Vi;wq6=_Q~6`BL!QK&kv&}&tjL~Dn%9btQuU@$msV|tzP&GW z)KSeSbR1Alta~kX=VKa5N6IP{#Vuu({09uQ54yYnj9^D=Ks{*tB@*^zwg3x)=$G?s zqhc@Ft;MB@bz0D|PL@@uCl)=T0ink`FNA=gweeKuId~HKE zR}cp)n+1UMY{44<#Cv(o-`Wzl_WxlmwV3X(ReZzd;a{Rd%CgzK2gd66QN~iE(;uWk z`!5F_3zLqRl+B`^i}Pio;8Onp#!`Ig4x8nrYomY}vUM~v2%Y-yWAj+qXxQM_v-z9` zG&7C?Wl_^6C+9bFZMJ}@FusqdE-5Vd9q_RGR$&P&Zoc53K({O*`_15J zv6|+&g?|nGe;?b5X7zZAs_f1si}hJJAjNwB6IdW4dn_!F^B!LFKnYzvoc;(ben3@9 z{TDDW7=EyjXpqnp6{26U4P1Q4;lIF>t3ExT1p^)Zj5H@5oy5w|?G+3e3|bJhKEh!i zy;whbdr&u{)oDO?deHGbs%NxV&xSomPS-J%`#tu8!>j&b=>e_j`47_SdoKrf53Rkt zJ!gx5F8LpGpBQ}m_gow705%^|Pykb%|G&V_oTrx)PkPnh;Dy38*u%Mk{{@!sf#U@+ zU!U7~=yz!a97f5IG{wdo6$5KZOmZ$|`+a{WVU6kJLzm~~q8-=8t%ZMJ&8Y7B;#@XX z&P7>E)Q>4CtDG}z1=q7ToOBGPA27_`T8Di6gkqKkz;CIKx$4r%1$8j#g(FbeR#zpa z_c0e38pZfIa{oS8RAc!P+0$p?h3x75pJ2O^Ds`3y|tkUL^nSP7e- zFQ}9oClb?J%tKL1FXpXzPTvOB#R1a{J?A4}6sWc9+-DJpJOQkmVWd>hI~!gq-qVRy zc5yjp2Q1zpo%>JI{G~qXO&`S-zw}~0>JVTjQ*SOWXoQ6}VTC zmICC#UYh8k*8G1)As_%k?MbVr ztT7J)FmJ5b-FIC{nqyYdOOJNg)>$a~qQpiVSKERz0)S_9{^36tXHG{uh?qxrn)^H3`lvQa^aE9!~gKrQ`PIL zhoSXmY2#y}2ZasQpWwpI6-Zh3TJdn++Daa(t6lSA8hrU{UOrA4&#UaV{fIt1bJj7> zc(y6I<5DrHrZu=w&5*Uw(bpBV!CwghsR6noC4=5dxu4@AuqYJ2wq=1 z<6LRo?$tN3fZ5?^QVZq2PL~qg*@wz8b%B&{Ekyjb`q1H)azpS@9%#6|EKd4Sv|tNk zpdjTVmTwX)2e!j?PE@_JxF49nI6>=gu8cp8LIVZ+R*R9YkP>vAQ$M%}7%oBM z*-XD>n>G!N64d|Z%H;pDW8gI={KArfKu5Fug6e2`c@#M{5edG@bhCHP7a!v!fsTC$ zK20}ArlLk*(if>Eu7|9&9UMtdJr2&{NfE5?NVsIgpe0d@@A^qL#Z%w5T6hcZh?6Vn z1Qt+QSS60gjuz8QT&KRX`FxL}e^+jJ7?csnk)n7#Fce)Gg8kEmo_g{P9QX9>*5EMa z`BTb%mi+$kVOjPu0UsWf47D{-6}_U#^a4>0xeD;7O6Y=<77y|ZYERhzO)p38!h`L& zrI)5x5rbD(`iZ?Gmc7f8X-7E99TuyEsK-sIDi-|Z;eiD0)@myDgUtsGK0=_4ix*s# zml0zeHHz^5uj}F$wmeo7hGXBJKSDk1z=Y4QuH540*kmy#rV%_nrL%Ac3nabSoNkD`k5;M#xd z_C-zZh9nZNPin3qC+TXK;eR!n3oEIMEk=2VFGLSl`f%1@+u|0LG-O{X^jhfT z-Z>)hH{%y)+*4d^n!@$5zCd%pwd(7nf;T~ZR##v2XkGMoONnYhi?WIt*J_6e{BkUVq0}@oae}$#tffpBRa$(fWiLPo67}kwap1b zRf?|d3!_(cg>dNRISLl8ZcG1804T4}@|3Lzg5(Ac%)qoQ)@fCWa56fc)K%(|I#kVB zyQ(^TJ72(%|W@iA+O z8RPgC$jENo!uG1F&eJ2>hH6|k07E@eC*#q$Xp|uPYXi}%sNe3wZwMnik@KX3`RdTl zOhq?eIe>1Sr* z2%bBQ#uxamx_8O}at&=DXt&fZ%xqDyDM!;ClBD1AnE`i?jT0d12=Ub?D7aXATY|Ox zSi(b!W5&_i%@?Kyv&0nu8H47o-OV*?M^T#j-=kYv`f<$~V($?)7&4^)Ytwzxj4=q? z8MyI?>bgE;)xBcJ!vWygxOke3vxuRj;ut0PN&nXdcdocwg-={T-Rzi}wT*rA8=Zl9`1TUzW*l>E#^~aGMG6ztS!0S6; zHhAuI9dW)RW=m>UeTT0K5*@MaPv3XJ^3B%=doZTo5u-hLtG_NQuC&3Og>m7xwUcrM zI5p;L&Az6%f!W(?{p$XvC$1phVrS{GaYc;wAFc%J`)!2$ZA4fp?|!-1F2|O-;f*<9 zGEb!Jau+TGRN_v~0I(VQ9rP)EO9~Q?x)gRa)7qB_*L9|srZ$z@B=*nKZ zLp=iUPn{cYa^J-5moB_<9y?KV4x7Rb^}zIi2_K4PFU;T~H5=+>Z-9}TG`BQ!7^WN`WwWHO;7A zHxT+A-Wk=u!4(JTH~G2De(v>we*SiNYt&Qp9(TK7M0jE# zO`a-K%^?TP7rtNrRu#ok`D>Xg5F+W>G_Fc%U|$>;xB3M26VVXc)qkfgN~5y;S5?3Y zS182D6ZFVy)?{f#KUk^q!YWsoO{GU2)3~aTLHKs~t<5tB6&Kb_xxG|=sd5_7GM#EM zqb@cQT;xqBEq8|_>~Bj;|I9PW0lbA-0&-@&t2#?QX!?HQLt*O;dVNfh6X?^B3wqq? zV4%tnC%?p|Wwj#8U6O04NZI3ZWkv!=$_~6d*I`E|KbKhg_EP-W{1V}NiSIw#a8}~` ztW7sd8j)E3Z8;UrMe;^DO%+i?w3^I^E!Jk=Q<8_=g5qDz^v0TjvPHD0qCB>(VJGDw zhf+W(K0j_W-<%x^^b38w1nMV0)mkw6bCdDZ#mr-tz8`RN2FmQ+ipz@^DwQz%V|A0w zT=?WZSIGs)1$NLWiOx%jjOL7)P}cPqvhyBIjK<`G&lwemNbL0fG^?xK>Q(KA_x&Gi z4BH5o9yH581`8vY8p%zFgv2KVs>gK#&)F5D#eFvGmiOp@U29*_Zx2%cmZF(IlJ5KKY|xDUTraon_yEl%s$gEX?D()BsQL|@ z>H7oc{++zlva7J0w?9BXKK`O*h4x_VdZAC@E;{ogNlQ5OOd)f(r3NrY^r!Q9=H7O+ z?58Coo|q{vd;%wu%UGJbnJo`Xp_xSQm;)@o z#*@O-?B*6eQD;FjA8W-^FZjIFwh0U3#S((=`X6!qcc*J4&Kq9kV@%$4Jnb=ovD80h zU3sbVwYY&J6RQ$Fj6y>6q%#oXcmT&$zJ#47=p{5}tGl z*39sSep266wgkWTOC<_twjw3Dd_fe^Ew72MLUGhNZ~Pv4HEx(K&{aV*iFtpxFE^-BMx`Mh4$*lH2qa6_(h+(l(8G|WXY1Q5d1F} z9Cf-8e@b?nuV&=tWq}Crl7Q zce{&?KBv4!XC^d-=q0m=WyildbQ(C8IGHQrJ}*8ikXu@6m{qcyg1bon!BD=NMUxae zr!{zkmRANVffM%3nJ5TSIdOn0RaMkAw#h$(uf+PQEziw=(`SQBNiL>3+Ba6 zZ+Ij}<2M{)#RszVF%DmvZAd&A;ok=v`rkN_TMA@K=FN_f{ngol7cjEYVs& z!IyBe&%!XHU(FMwPo=0ap5d2BXbw)9T70+S>#k`{X&;wek6r(!rFheB{bGxZF|iGdPp7{GP};1B!L>c-z~SWhL_sh+>}}5PNGK1-@+H9_$q=a%e4~cd4?s!7p3SH z!K4SOtWR;pbIC7#d-ik!Fk-8k7)QEa^kW9xuxQ|hblGL1OP7rw!f(UR&Tqia4)!iSpH2OM8S3b~f(@da8HW&F}`BZ;(`4J~{RJF$V<#y8iNRo-~0 zr&z;Cano_5i42*2E!c!ec`5Qls=IJh>x?<%=mlrGl2rZqDN04kjDw2T1qjZVGDlcw zq(ft-I5MC1$lsA{A2FJ`8)N|nWFsi>Rbq{z9#aI_zC94uxWS z6LBnGbk_o$Q>Y6ll20x2(%eWcr;9{)86L@g(xLj$y}3N<)=;N1{`s}Qn~T*VI4Bu{ z!aT?PqNsx8BdPw1Hxy*MJDUuE9nlS)f9H^ zPh>&krz)zZpF8|BL)|dJ13R~L4ASp>wb0ijJt{cmgOeDyB|GIOowE!5ld8w*1f-$< z)ZILb>U1}ZPAdIVApb`8Lf7+3O-^O!ahldT9MK?I!Gh^#vof4Vv8_gO zc}B!YiV(H<2|+D*{6lU*fO&H z`&?uc`G|<;Xs-+JQ3dYj$Upcl-{?WcwpM<51z+J(MZ^X7N1Pw95q|KsedqvB|q zet`hNJy;TSad+2Xi@UqKySoH;cMBQ_65NA31PvNAxI5e>$&>fV`+fKRanBykEIq%e zs;;i8uBo1#4jD?8=*9l?*}1340J6v1lCDX1rJmny$;>3f*q|)~T7a(c$E0ggUFkgw z>a|Ud(I1gritv{4c0$D zw0aplPZ&dzfi$ohX!QVPAy@^pB7h_?CGMGdS3)4g&jw5Zv&(x0YG%}OfGUtW7%c+; z+LkOXSca4DE*e3IhyjExrFeTS&=J?W(z+X#``iJDl%7xI|Dy44qGqEmHtxpcmcY5V zDw68k-|RpnjETS{Ad(=dVO7zp3c4maZWzbU1eH?V2TToXJ2qVKo&Dd_!fK&46|4>I z0l|6-=Aqwb6PI>>COz)CAP~Z|k#8M1;?3T`be!B*_~hdMZ5=cgflRO_XjHk73}*F1 zSmBb8EU@Ni_r$j{0#bRo%V1N#~ zz>YXy5^xF6k4AKjwDIIsfajLl>@Voyl7OVNQ{5a%!PG#;Feh2THSZ8 zdhWwISCYqo(?D5$_z#EiZh9dsegzshH?&j02CzRqhKx`~-zouxj8IxIBaj`|F0MWt zEnG5?8`drE5_Un<4*z}YM-K%ti_P7QI~{9Cxu8r)E?DQuL&lNr#S-ar`P-r_3)<+)V*&`!;rhq-*t0a)v%vMa6;7bYKI}{>4r$ zVU;Y^4bvHIA20{3igyk-$NP#GQrq>DOv%0BC|}U8D~^aS9;jZR$nFE@;+^}<@y77B z=st`-lAA&0K?=c!qM?uwh?7y$oqq3xhVf0k&&YZ#XD9`GL#4I$vT z!U|=c^|tG|4N=aB`X8MNW(9JWoI0{AVcr8~_`)uJqG0VN4{b&a=0KtDAKgFWV4nv= znFrPmZ5_}942?%2JGiPoIlf>YuJ|=Tzla$wI6sTgQwoymx7uz-p^!`h|E)5#`+}N2 z!gNp<_I~Q(xeJ_kjh@xxLdWrgG9jPI{-_U=8#D~?S#z0AZLn~5pl^_@yZa{dU&PV< zq#-gLuQOn8{z$h0^n_FjjUVmxw~7v3m$dR)+LUO`7qFH1<$5pgBKow9o zzJpY287HKW6e9EIxgEebFejd$%uiZB2z1U*s_9x0wgze)@IzN?<$gdM{SUGKjEa8h z?-=~-7n5~bZK@VT|B1nYY_QLP;O-IaAC~W>M)9uF`P9sb{ykp13!;Ile}C8JUIDGYL>!S7ryris|QrK3dQ70TgoUw_XtXl1E?_+EJ@2~o47{v%qhq&{K%ILTk4QR{*ttOuV2 zQBnqsE?P$c#MFRG5K@mI>0hYlc$h2Uepv(1{}T33A7G8ps6c%TOZ*e6f5t;!l2=LW zNZY}`=dX#sr;uOazgiVEZ)810!BnpcRmJI&RGWhSUj=jm{o_IJ@cB~Hx^W$d3Pg(o z0Dxj39CeMV9gz@_TK#uR41f?Q7#9eD{Ri^g;GUJa^b=Mtj}})TK552v(^}E@&(!sc zZyWFGzQsY!3MwIJ%s|}*VA6yMOZ=JNB!Se*zbCb4Vcf5{h@KTlth_4_(FfHuodKH{^*3_Ajto!;9msgFS(pS zN(oBL{suNf4=askk@8;y@f+866A$$d*MDUBIiCL}Uf_8~1}_~*4l*Lo0W&(B09PWQ zeq`#uwE;jeBl~>x;y}MM429(6#N^k|n`uOPk+s`&T=m%aD8&A@2MUjjk533>A)#@) zJRq1$Eq|5O`Q_)uODmGktqYhYixyS^wTb<0Ory>1`P7^fZIKJ;M_#$T(dSSBYUq6O z4?Fwm8AOxwE}UXfMANZkG1))*}ZN6XvOqvn=|mPXG8&ql)>y`LZBDciU;KsPWr z{(jNv0^}H=%_zlJoIky;KX5hlIJe{MdP1_)8XWR zEL>b=#&Sbt3LI&Bj)D76%|(vaG>lb-x&s|?YQ?{$9rLCG+8Fq_T;gL(eWP9vdltNC zL#q};IuSeAH%~$&IfAya$?=p&znq<6+VI1GY1JE`Eb;Zu@#lEEnm3^@9XJ%*tJ zDDi6b8%q|M>7Z3n<%9w9_%g|t39J?=5wtj3j7^3~bU3O%d4q23&bd19b1F(dBCXE8N-wQrMe+t@#lvHEbwRL z)M1)~aJq~ghQ0$}@oH^<2DXwSRYm%R1auMtbz{|rC#-H=m|Fi&5IAW~%-^G5^!uv4 z$EYa>K?8~2qzIZ+7D;2o_-A{q1l;WVU3t?tcl{p0PoRixTo*PZKHFBMFJa6Q|7?Z7 z4e1vtwUM9dNjzF^0r>WJiGwiDHVr!ba)1Z`4$z)tpyi0+S&xri&ly{AA_7$^iPRt6 z9f=8H{<3x3IJFK#z`*iYT*@Y?NW!on>9Z64qOq$9$~&o^sgxTt#(_{`ajknlM(ojv zpPBv=U#r~<;}_=Fh*@Sa?lVr-$s^c;7@PcJ{8jP~V`U~B;BQjDq;0wDbBOer%_{5^ zHwMP5P5$KapN{;&9yy=hTOJ|d7!m%fn}5#!BlD>>-PqVAyI}&0q$W5r}T8q%ipIK7xvW>##&PLX4q?DE~V9r(ulE=hJErwT?Y1NE5_iWN{nflM~Al z#`5d~)P{d369aj-o7pnZh{W%#{O*J;?r3yYU{9qD{s~CGb-AG6%|6h~#`6TqvgJSl zz!($&{?dj@+-Thx%6uTmk#D$vCT-7NJ4637D1i=t>n#G1S|{;1BcFCb3NodCI`&)i z>CvJ&#zH?gAERY;w-uIyn-y!so|T8a&F+;}O-5)OD3A{R^zuK~o?2Uttz57hV*Fn< z@{C=sIJ{}b3o-QnsmlLBCiRtsWjjlAMn^V&DhpX}bOb&QAEUS7egAdr3GsK)|7!5@ zHk`@8{u?lSwT{Ed=xDgpKOPH;PcsxaG^basuErx_a>V%}B9RfeI4q3jhSU8YV*k}p z79W9mbEnN+v(xie``^G+7NAJ@y%$}Unv?#25%@Q7I1P|5+~z{_>3_iNzwRu2g7_sK zwIn0_GuOxE_L_YV*D{e=*0%IO3`6pM*_d1claWbkeg=S{ zSbt0mA>lj0;K1Q^qIUwp{(qzR94wV{u+bR*12nda%_aCRVEfTWCMsD3Se$==YAQX( ztKCs$H7h;6SZ>3#-&e-!54Sn43{vh+@LQ6;+r~9Ooq(QYjG4%3mw3SP{&CXo^K^s% z^xfy-^V7}B%F`|W)7{hk%G2f16S%Cz3rGwwFfbS}+d>xLqc0BR>la{P3D96*uRyH^ zrh4{6Y->FXkYA!qbRE->tf8p)gpAm@ASTgI;=R zY2k8ttXH2$!KEmUTs|*2k~dGT2F9HTWzh$CbMS8XKsy6BMDSFBkeM}yufao<+75r& zTYnEFS7u`Mej%({X2(h9T9&V=5xL_v#!{Rh+ddACR^6(w8BiYI)a4kDj~^y?44Z0! zo+YYGoaHm+Cfye zqx^~eM?Ts_>tY2p1qDT$_nB2u56fwY=lMQsG6}4MLwb80omQjXb`3mwRaynhoY8V+ z!qxFO5e{0hO! z&I9a9dz*#u5gSB!#SUsq|H3Do?6iiNysec@wadt_=Y-tHxo7Zydz0HI6*VSEFtC2u zzrE?VR~Z>NK6{h$V$>olir1`)9ekODE*c>W`T5+HX{4Z{!{ zjH;gVIYOYgnQYCu2%xbG4q-AeMtayvu34V1DV#L-=QCJuK|d4+ zxpRg&SXVoNnqkJEdm2E9Fl5td!)#Qg)WoRqxuL<+1xV+OSU6Evx`PmScD}Uig&gDO zSST-(K{t5@xKRyYH9$*HFc}Hn|N15YUtT#Oep2fwx4sB!n<9}J7Lr7-ko?E;6=pbu z;?NskP0p!vyzpyYGSxz!#@$J({OxfI;RM|Y@@QpNq2Y$9N=~!4QAgI>>x6?5t3~g4 zGPq;d)Zgm|%uIj_+v&^fve);MGD<{dLid*j(olpaSe0`C-CdWh)<(KP6H zxvng$Z`OKwFgAgYJR33t8KeFcq>>`Jw77Y2LwXwO#L9jvJB!6e25*qKXqwtiAeV|y z7|KiC>A!_CxY&7g)K+7xuiY~QgoO8zMLyZykjulBCVCIf4`=(OAFVyknm}@9kXjp1 z&arzEir{uM905w_$KO{dx{DiB5AelbBUq6I!nEUyx#F6KOxDs^$mXPG++o&5e-g41 zJovPgl~)Rs@ONC$h{)_L&V9H{-v+#vtlO(ntm`sIsXr-)%1-1h48sU|k=ecU7SU#K zhYf4~VCgOS%U0qyg_)viZ`mv&)=VL#KY70Jk^9J(ZD0!%r&l5)awF!0zpvwO)P#Iy zT6H#X;gfi-z!QQ$#@463MHX@x233edJt)GN%m;f6X8|m9!3QnCM~{X)d=rbL^r}Wm ztZ&RwW*C9~vQK`dsI0^w&-hB9;>!?COHqyeNtVM>rP%@!+NZ)-Q*4r_JfHe)N9#Am z+x`1UxD9(7W@{N=Ia3&yr#WxhpX^bADFF!E*P>u=-S5QR!@m%m`MWdkDAHb1(Pc5X zZmb_9OuMo;@OOXCZpbe)z>MDdP?8@E(}mo(+C@DY*ZS!zwS}eFi-QoH;`tJV%?~-v zyVP7^RGJ~6df!xt#O$Ry)1BxORKIkS)PPmheBp6W>7gz}J5T)bM)cmtY)qBa&+mJr zdL;-Y-CGQkBB!tH%s=EvSJYbikHOoDMasN(Df)b}EaO(R`>J04Zk*cz>4yCqRt_fo zd)NtPUKG<$ZdE}}X;etZS0fM7(K45=G znd_J?FeFfffdhbnz5dsn{WHp#IXK#z={q@^*;s>O&4H?w4K5JvarEPda3{`&FW7Ik zS)FHM!3;urdNJwVdzQf3gJ@YKu9QdvLGE{P; zPdHes2IXWKTxiB>I5L)F)q^}z=k275I95rKV0QtV(moBo3gu?^;E;XLhuO}-clhSD zv{@;NNcN4n%IK6~sTNx>??3KyAZ2^zgZ9`dkAAzP#QtSg191*(pz?C9V^5@g>fR2fpNvG)pGsq0c9K9>j9nf`VHg*>H+)(nZ)4)E|O^IK02yC z*a;ODZSjGqq-=nvRI|c8f0Q;!;A~Y4nwEujtNZ)aQ0wCHF-FOL&z1cGA^ihbG@7r{ zZk1`09GODvTMMa|iI?_`B75~N7nMG82g-L#E(appO9vNFnF@uTo^gBU3z3gr`2pMZ zyXVaA)=LY0_9QM-mMk{zY`Y_L4viUmT!e!dqmI>sHlg*DrHe!fYpFaDWWm!%5*V4@ zeE4uas_Yv0Xu!c#X%hW!>@QZJPsOk+v2;v`Y-6eB#0+J&1qV+#@KH;E5fh2@GkG_H zdaZ+#;tvB!Be@%v8;XHqtDb)a1FI@x2~B4 z&Z-^zt}6;*h&R2+utzdtdDecoF!aKit;|5po-E-E!lpw?hD-+F2qh#LY#106nTU#> z-Lx==amK|>=I56vX9rny5#wM#eZUf<1xAf+BL_92K+`n}MvnAE+kI!|%`l_&9g@@* z$1!0LR$1?B{kAonW+oAd*|h=RSa+8nM6=TFdeT*y@6V{oj;X2B(Wfb=Cq>MUI`xSo z!n|YLMa0r+8P3A_0wHl;BXeX%{Sq$?wmxTX%#s72OD|%UKgVAB4IPv}{Ia`rauj|M zc-}C`i(X@bwdnipHtc{2`q#)h94olOX28H1#|uoq=iBU>Hy z`9hcA-Q%~M;iZvk<MPPCYZE zWZ@gOs2vvdNGe@wZ!uZL<%?Ty;2GCu8fUWj`6cL1>_=`tD{iq%7}=n72b^E}DV{fP zo>R2aO)V*p)wR#_renn7Xc-p;sN&}Ob(=xYV6Cjdk207>XBegk7_fhh*FL7JF<-J2 zE4m*5B1h!k1dc0{C@jc(5{M8~ad&ZW?xz?{W?e_VfS)IKa%}0R6#`Dt7|}|o9@^2<4M?^6(}TOAq&T<_iM&p} zysLE6%sC8`vdB5X`40w2)+Pwa2V$0hnmcm}xxK`Yg*E zqbDE~lP_7?o38uf&--8c_7CBuC8`qXyu^$qG-p|LU^!+VoEz#S$9u#5trOzI7-^}w zMy(~Oi5m0!z%J?rmSZ)TJ#4nr1HohIk#}jsp2WrDR$oS?ho$o#VEQu38b>SOWg^lK z?8Fx@g^aaWJe(){e_Wm-*ZBo4d9A+FkQhL0e%)No@uOPA#-nQ?|7f1Y{s5fDx`RIT ztutTcTa^v!;i$O!Oc<%Wd;(nYx41;Kb#gy=@=sl^0+EnUcraiWK#x-{V^$~(9FRb#t+wk}-ZysCkOHjso8N@5c zSN6zK!|u-^IhgQ?d24H-ZjHjdHWfPC%BMR&q6L8pdHMaSHQP=#@p~y@H5wa zz=rV`_+D-a-1xj2t~;IT)TDYc(v3P4%%VwSvhlzRaQJ~|ip+h~g0+A?j@n)f;f~Dx zglJ1k8^jxWppP4E*RVb1N;hZ6g}-gFqDU3w^^S1#`%8b{j~7{1Foxeacrn2=KF4}= z`1Cz&WUX#DPh}5nj^afQ3@}Q2zf$zPtgdkeS;9!NUtiYv~Zy zh#tJ9X;O&r+O2{tGsvRSiM_#3xL>r7TPLrkhAQ-Ho7l=*yfaa;SJA`$sDY8EFXC|T zfaVFPesomPT6Wfg*6Bv)52lbdWQ5KKo1;|Ig<^N4y!5T@cRmGAL9wIxlXuP8)E|A$ zKAa~IQYeG=v5wZ?JXq`OcT<$;s`6ub_Ct5jUj=7z5TB=%E8Fd#NNiq_D>09 zM0v$#K@7!v`y)22FnHp1PH{Y~)EO9tydnWiXz2$R!Ehd=vNSCVm(2dF*_CoDdfjF} z4^F zyGlXwBy^Z=q)O&4uNVXH%D+QayS`Guq>9)rjr0>~hs|7E>G*bQo{}$e z@m@PgTn8@ynln%6b<<54H5hu4cUQzqy7x9~(4l$Y#Cn+Nkr;J~+-Ov*7GD%Coz7kn zneL{u={vuyj_5Fgz;M?rJ}w9ZCv90N$h)pMZ@|}30B{fXO|pKdcPCr#K8Nl75Jt(J z!!Nu$oafY`1&8N=6(pjID`+v5u34H(@~J z9CNmm>id$6TtrK>U2F)-kOkqx^i|aWZYAUfFoI4 zuiE?D>)OD>bDTDp?XyG28&A_tcrxpX9lynP{9%uMYhazwBb)@K! ziG&Zky7bF%Cg+ zhaOVl%ER2S0wU;2^$EU6#L)+Byk7>t5)hm6XZup(@!<*Xf+V4( zi{^*NtXU`iTYLR=6Mhy|+3T1)NFIa&MVcL;Rry|lMD;;*ru1FWnMd=B<5~iVuP*|2 zlbNt2T9wZdzxf+b8E^L%y0I!UXbmB7&ZLGn>xt*XCThm>&)a23z%$qMsFr>G=4;kk z!%L16v@7?$KNdQ9E9w14xA_;dN0qYR-j1>TOO8{QsMbsA%MmrJ=E_`i>3o`R@9xg& zg~uJwzaLH2;F+JH$bC^DJ53e$tK;Di9_qVKoA(=XtF;V2WX2!^oJs7zdz;9Kmwteb zr@dUxX$%YA5Uw>x%_y=O%`&`LzfeKW&pAV7J$@Cq=71a&PFA7? zW{e+1Do;eB`LVw(WyMa|yY{ST;CPTY(;sa_lLUW4BEbxNPdMaOa~hHLrZkFR0s)K6 zi1+=#!rHK`l~_16eQq`i9Zd*FcBF9YSaL+bfZ&@%0>v4!O+}@BFZ2nhty&*?@_hP4 zWBI&6(3Y}lIb+y)>D`2!!}rrCOf*636)#iZ)4?%Hl6qT`y}#a^5NrpyF!lo36#cWc z<|2d_8(Wta$7lz68#3WriY{~71KXCWr1dUTs6S@TKIvWdQ;oTAu5r4Tn7&u#S{Tok z*#tb%>T#x~Pitkk!TNqbM*2acUsR5nLbxl3{beEMQ#J5wiY2mVKDts4tEUeica z_jG@Ut81K{01Z&3*7_wF820}RSAQZ^rs9zeE(glPs1AcXFQjADOAaM6mLD$(V)BT6 zRb>;kWvGr0sa086KeYls`#kWR5i7!qHEO`ZMw=|RxP4D8D?7u#fWFFyjTC+81mMZq za34GgV|jiwSE*HHlB&=I(qLRjjRM~Cq=4$R0unUYbDZA{E%e@c49+`jMHV7d${mE# zPfkiSES;cJ)fh<>5%@skKB8aJqHBOQho`6;4onu|dUG%{w*=n7%;an{Mh!p4QTP?< z#EMey#Z0|mXl;W(B?GdVV>IDpV; zkXr35*)_pB_w5$2NskhSEM#g5)*k6--kcN-7+B&;9#g7NRz;A)z{TvP?5_1- z9{!31Jzn{hk>pL%i%XedZ)2&!O9MoMmx=J=Xkj$ULz)au}DR1Dadwa9KA^gxNjP{gMWj< z#gg?gx_uuv5j3o3k}S9=cZbC*uSXxxqO21q$da$*l%>Nih&~icHRbNE^>nuw?c98E z(3joqgWIkJ`Za9P-n;3&gwAWyZ9Nh6nX)Q>v@dwZSIhfLGb$6eH%|)fbaXS*=hp6+ z=Q@8oWXC)T^D4+8pGz@+*>Cm zBb*RV$Jqpjk(6x+3n#*ejM=S(rm}uLZ>)N`ch%6`Xc!Fq4BIT=cl6nun(UJu#V0==SBA?>g>;@Js37b>dfU;3I^T3SbgoRFac31@w%ED%w3 z@kQl=YiVuS^!4$Y4ZkMhM+F6nv_Mr+bZ5(K_E9Rc*9R4v2QAgFR9XXqObPZ~a3$*M z84Eu{24}V8o^;wo(EbQv7OyO?3dxbzPaR0@ITF~q5i8jkX9?F48sA*Ot}}%B8PECEH2zX29$kbp;& zmXAMzt%G4Zk{*opo7THOEL)<^xp>s*nQ#^!nhHwcvXRwjdo0^Xn{sDIh)zjonhts< zu!p)yZN&G$5-7YNF}M_@Oipg&_jn6an!>ssHn9~-Li!$3+0f%!heuK5V~u;PJ?43q zq2R(b?mkCNVFU;>+4;QB9;)Iu0?F2CYYWA-PSH*K*gDwD@_mlresnAIQfi4d!yKE zx@Dh6;)w+kLX{0URXt1!M-~ht<4CPuY(0$d7<9pF)3TiP!owA7Mt7!adC#-!gSka< zMK5k6|6`?~D6~wa>(=Fma-Xq=%}e6uyudz*uid`TcT$9ot~C^U@<~d~`$9Xqhg^h0 z1Tk!{l2s}bZ~9nTn~1vb$49;+vt*8?=P!KK8J>$CH(4OR9Ns^{@;KQdW|1aLdKmp- zF`G$-<4&Fbtp)+jU?%Hhk5WpW)R^z5gRxZFDbFXLC}qVm;rE;p|Glb8aJR%E2LT2a z1iHOI{r6mLV`Z!Na}7vA%4UK8c}0~w3|@w>ZHjl}`2 z(BswHGgL~Fl7-eeE~cY{^pDy{4*_Pb_%zj_#%qkQ3|i&Je8&QoV|#is}*3KM5@C&ksL8JW6jS63!cI)6;I!n{MTb&t*fBD|W+ z+JT)E_^=+&8nLn~m}F(LQ09-IDQs)#jM>DkeVF{3x?lZrDV~Vfy!=Xy#sQknPIvLUsGE!P_zZ7!zUacHkK&IjnGUEdd& zOL>^(hyHKcH2f`!pb9uAm7CUTDM>ekbPTnKiT7y|?-op;qrNC4@RqYu_-|s$2)ubY z5-H zoGy%F&>A*d26zJjJ5ku9#jrwkY7r(;^v$f&P(ZqWAxnP6-h&3d9A0sq|Jzo^hTkUa zMw8i7SxB8SU-S**hWa?HA#7o0G#0U&v+M1$W3pQ1!v$)I=g#-Dlz_lnB?ox4;4!0t z=`(eU?*^q6dxH2cdZVoSzE6x?TQftm!fNnx!X&Re*=4(D^=1B;`BFdgU1I(LVa(x- zTB+pZ)dLqku{O&xvtidH6Ead;?ihadmB3b0j-O~<;b#W5z5d(PMc*5E%0~V?w=JWC zaHh=_yaU#l5GBgJz`+u_dzDM3Ca&Ob%j6ki(KBT!eD_^+~qCXRnCu%t!_y%J7^g@Z@n9ObK%UAJ!F94Z+HK zMe5BGA8x&n#__}pm&9V%Ptw_#j^@oi)A2cC@c}5L;^Z>67?z}r^HX*zFs*Tdr z8e`STaZ}#|In&bkKp7%Djm|vheu4%lLoA(h*o`bi8@JvA{o-|(MEB}`aBzPazwY?}?AQF$;yR!wG zNNbdv`d+F8*t;#SWs%%?{usr9#8>r5KPqXMIBG6!c09%EIAkf9)hY|PWEU>)jKfdQy2S_32GBuUxq>o8-kxul?*{Em z0emci1}|^yg$6%7Y&47dTa!(^9QKyieHcO?JtM)STJkF`)gfh;FP!IboPCC z1zb9|UPI#T2nDX#KAL`Uv~A)2fQ0Q^SX|(mK?C=Gl7vTC4YTlzJq&eA2kjeP#(^bm zAc_GsC_$XyfGM(Lbr%wGOqPt($63H96UN=;Nemx1MR@p9$MPmw$VMkJ)W37-y#dP<=|4cc zeOQ)y^7trI+mCwlBbUAPsc%K5`Kx=StJB;#-H$qT&y{=40^Z6-Gqw}ebBzpKJ|j;g z-=Ws3$I!5fpdCb_lf#I9d=oVWb&Rnu2ABp6jmSHP(Tg+xm4cN)llDr2Qm{+V(<_+& zwsTt!W{y91M2U)v)}Y5%yskixuVB5joo;A`x5PacP~M*owTZFBaAPzzGej>G3n)PQ zpwljzL}#`Rrojtz;NW&JW#N-NX7lyp4^VEeAF>=yVCZ2bT-=`{-W@ml#pDFfXjl9!4xZ zfq|@>^>X^tamGZ`0&!cr_#Cc<%35hfv9zOb>*AshxE%v`jx5FdJylT#uKKYImP8@Y z{m*P6TmARSwk|i5ocHY`>5jYR1{(#Xr6J>L8 zT7M;`QZSC_F^oX_oOuzKTvch9Mn7j>SP`fdsp5TZB~ASN)DPG+ZDUFBaK$p`#1Y?Z zn_QdTQ6~grJy^sTRx6-&d>Di~ph9`sVTdBS5{r5%`h8-(#F+05O;G?Kca6FzW=pRH zTX@J0d&(1M%w&LoKs+THZB}0!?FL|n)ZfKF5YZYl$Wq9c+lmCKB%*OLZ0azGhAs>t zW2o{V2YPTN-K$lM1KwSfk<7|7S-bo>?^(g1vuE>>Zg9UfgLh2#?B{-7?TIco5g$2fjI5n?hPL{}y-2=8u zb?&9XdAebUfc6Ux>bQMK1ces3Yk1zh7XNqVhIqX0=jswEq^Nf*$!GF9@SGEMfZ3s)!ELAC~F+8FwZ0Fm$XU6{VJ%2;n;dOS0*|PpF?@ z)A|uH>jH+IjfsV`_<-mqiTdUyN^qchDNdRT+&-qWJv=XZ4*e=;J{chbJ?cH_tD8Q? z)wR*IdZHXh%V}t(a(G1iB`oaxBC$18D9SnwEE(pt|3R+HOf+yP!4grRT6_~X zq&=>&N(f`Na9qiLf#kFTbP6-lSz%>qJ*r*1qV-F;#*u^Nw#Pq>`4AGDP>OgZ{~A8? zy#KSq*8^wT<`qsYU#w{AZ9MD-uQ~*BSdf5?pX=>)wzRY3z zQUbUUsqJeJ5{~Zc^YK7u`KF$*gI$EnO4UyZl%~wM-aWO!ewS2OEL1kN`j+3ogR3;{ z)fI<~6bvx7obMEmrC%xe@2A&9oDq=OzVuOnhCL~9K5s^7$#kzzwYiIA)^MI&#+eA9jq;xDOqt;v{~gb5J|>L7E94j?+d^6w~dJ*h6JCWxGGZLcd8nlbF{M0Q)>4x zxd%%@fLyTfD1``nu5>5O19NuuCyd>b0JZ?{Cm6RjfJr^(ZaNJ6sWAOnSF?$uGXkhD?zNp56N6x z{*I50f^~~%L!2V68a%1yE*#<8cHGF;*yxkOZ*dHht&uNhLGRWgr1Q%K3y*lw!M4S81kUdajz^;7{(2V7yLI@t6{raKBHed( z#gOz#jEnjUlGgFF8vDBaZoXV*Lt+iuDbx?B(QG}CJg<${)z%p_CckjOK_>OZ6~-BF6ti?r-jeJ5nrtgjQqT4~>#hVuzg@Y1>av)bz?Loy;6eAPeB<_oq zO^Mzo7cS}7##kN@T-zF=OTy|Jv(+9C-~9F(ES}onA#pE=(ZnKVM9+%T&b@cn?X?}} z7)}q)y(hFC^jK<7Dz^!m$*E>g1KgNWXxXu5Vgu!vQdrrkW@H2I*w@gq6V1#9_g;YoxGK$Vx|J+vEsm58tL< zr_QPgt8p789o^ypW^DZY3tdELjjh5{@~HAHw; zz9xq-i|lq;H4D{gu3ql!biUDwPpR2y>a8fz$__WP_SvP4L#!*~RU8|lQ!+Oa%k^84 zpNywxJ=6G_?Y;#qL>L!@Ok}}+O@0)Tzo$KCygM9Hg_>^;XG&i|ygK>6ruspUfxjZ)i>Hp)%+Ulc_;0D|8 z&Nu6ERa>P9!S2Pr{wc{fU)VrJ5Dj+Ks{zLimi>d)%d?d@-yczzZ|WX}ARce;jn$ZR zVs%;7oxP3UF`ZrYScuos04+>EGH%{JMHwD*e*N}6F~018%T;H(#kG2GF~C?pI6@?5 z2G7!L{@~bnBoP&`U+;(~}-x)Psd@GVAsgfs6S@ zWZXz^CqK5tc|WE+RKHS1cAw~X}p{CH}I&!?X0vt_zT^I&+&%JzI^ zT1_j@V(ac&ba9AkLh;^O`_sj;0FU^^amRg#(BTJ@=5C#_FYXPk#iE`PpD(?nUc}YA zB|z_cJGqEKtOxJ`Ax?)ll_7W|#UPGn&U#elufe>J%gFCx|ISRyJzIp~L1AATw7CE8 zJ3}KU1M~l^>K7_oJ+JD69*@Lk!8doDepx}Og}w<86nh5`d6Gx@L)f&suS&%O>!32e zq5aYLp)1^K;k+qZS34`!&DeRy!~~R;-r~PBz(#U^I4MU~>26*&Vb}ARVGp>0UXwmy z!J-b#I!A2?7W3ZL>(?xqS&bdG!YF#tiAXkN;n^c=E$4^OF|8Z(fE{@`hY7?Gu3p7s z(u9-p`Knxcjtt%{R4RE$VF>M|NR3Y)Q9r8EDVtoCCdVP$FmFo7;#ZsXJClM7h=~J3AvyJ{kW7!47Ud%seYCBB|V=o z$c4rk>17h;pa6`}(Xg^R4w;?ksL(0LPYLhuiFeNks`FH@6{AKLa^5?3uJxTj1b?7V zxDJJQrNk7bLU`5h*3W~1>t{Bnp1QoB+Eg7jd@8gmg627R2t`2F8u<_^9uK+Plu4u> zH3w1Bq1U@5v-C~9kCY^nUu;P>s8~ayvrQhqK}?UWhv$KSpYlAc7e*PTx|aY+BapxT z*oeNpRbx1pt1X0XX}6XuYegr9hCxdGhfZ)$zEwYLN~yeLN+24Uubj_9GfDK}S6%j$ zmBoD<=rcPX)`0Y83P-)k%+Z&vU#hr*2J|@_(=W7qar6bMNot}t-hKfW5Q$4UpP%19X{(mYRL$meHyv0qj3EI-35Jue&&n>t%o3;?@NLLv(ET+W-ey0#6z^f zwssm^_c5!^J0u*26Pg0d;r@r~Bz4tA`Ls9nE6$t;Y?&?xNFDkjaSAVQ zzwcqu#@2`ga2kCKmv$tUWrV=84->swkUfHPt373vG-K8vQ4x}}W_|yz`y*Na&*bR1 zq`cMPJNVFWmQL)5l+SAr@Sn9yQ`JVur!Z(CYhFunT{gb+nYd$aEj1AHW1N$dF(Xo>_NGfDe=SIIPcr4z`t+=5c*rG^lp5F&N%2LUCMRw; zLGq%)(GKXv@#P16E;yUD5v!kq3AgFz-&+KwA59kwO|@{abY6MBWluJxzN1hd3l8KG zWb7Q8W3PGuvVLWM&N2bX{=%wACF4Ef9aNO-BgF${tp>h3#l1ATDkr({uO}ay0#SnE z&7I+zRT{M79aUy`YBfrB{fc?c@s``~JhS{45Qj_$rf93E=*=c9;wjrsLli{=Xm;AB zM*9ziarkx5T=VR*M>)d(OxCbe`%AKe@oVOMiG(3TePo8v1oGGntH1X_ZOR#6I<<5ja`kV0=35-{1)65M>J{)s=pexw7KjxhI zMpQEsm(P9NvDjLS#puSj=cIQJRBDp$lfw7+yLspbC=}t&*Au00p6(Y!zE2d4Wf>lI zPh@yMU{!D#hSSdt3yStp3Z6sY`c|G2U_Wq!lJ17m%Y=cOH!lG5;e>gv!>%r z%p%*HR^KxSENMp2g8mP0Zvhom_r{Hi2nMMjN*f3UAr3Q0iHIUdi$!-1Aten83eo~f z3^0H+(kTs60yA_ELwCafL(g{xFyHrG-~Zlq*Ilf|?DN~d{nUQ;b8_bRbcbJYTEAK6 z9I-V%Lr(LangAkCth*ynN8=ajFv>px5l`JQFZ;$v=;%pHBSW5s~w%T>;a z6z?KL+BegQ?q=s_u0nz6HqyX$YZ<-j0RgQV6qWgk^P&T88OP*;iN&pK$}$P>?RW@p z)i=AWZiF-ARy}a@%jFw$*a4@-W$(cKQ#`mk98RI`w27{h_0K5VTiOd>WKu^*ZSEBdv+eGLQw%zNRoI)~c5%k7)eV;8 zRyQ`n>uhi$aO|YZ%3iTq`PK?{5vShkys?2S!1_lyPsA;6^m=6ArrSLRh4yC52ZwF^ z7w_D0Uawnp-d+wD7Q(4MQFqzsOym;Yl~3NCXy3;An+fkt6uT6f&1EpV>}+_{Jy&N{>ITye9@auDPxY);g-M99Tk>iVZ1|DqAVh5czhiA=j zt8=)Q&zB0YjLy5Wb!O!|lUSPvE^B*Lj0*l-i()I=!h5)snharQj4Y#Uei_+a)#$g<$xNh-n8=Ako)NSlb2JR)6Qhl)5220@bRdUnx zf(^mm0@j7n6v*|Nf(qBUaeKDg&ccgP&%2^roYS`_OS;PU93QzB zoORi&!O&hrDo40%cVhTVHZkhuxRvDs7jiX@Q4*r9jpmF93*o)?QLgmzt#%3^*UGTi zkh!w2W?1J6m#FpdZPA5EnXj+Pb~|?mUHCTBoc9XXc9yX=TpMdHR>O70Ho{xu6gbp5 z+=jQyM(?EXPNhF?*-RL>$yK(oW9Gc+-R!azB+6RnOVE?{&I&o>pKn|C`?XWQShK@l+ae%Vsj~aF@4g})M?!| zVs8!|DY~tgZf5Q=nckc}TjWwOkk31^mr+F3ErGMvk2I^JpN=n)BCbP5)W75$8ZSd` zkC*M7#Yz?iaMUXg&@Kcb%gl6E{4Mo&Uj%6tx9Lzul{yHHmVI<6cWK%NnY(OlPL$a@ zPcp6V7B6gHd?uA%*FJD1^XzlxcdJFQlaa&LXkp>SM3;!|c>yS|%>u=pKG1ddMzFvb z^C@0xosIXobsQZZ8&p3mF0@&7qIUSU%@~RQ@U_`QQf;Lk%R88k)%;24qKv}A?e=a}2dv9A9w2mVg#!zL zMSl4upE~TqLj=edyXS?gyity&3Tl0g^2)NfwU29p1FAf&mVC^G2_BRuWI4tSRws*c zT2X>3<1#ioG6r_FhW8XMEGOjm;|f{B;5voxpxIq~5rrksoF^D}Tby?n($gGg-tfd& zVg)FA`EAzwss%)Q**YqtaH^>9|m) zA|20gcIQda=JgBbI_s+MyK-LS6f%-goYI|4kFq4e(#u{Q`~ z>CwVFe@(g^1#7W{FPDA4{CH9>on_%MZ%Vkc&7j+b7p@Q7ZYxcQA{a~O%5IFGCO(m6 zX9H>cBnT(QCOEw%zbyEo-`YwzdtiPFeNPnq2xEd|y#7YOh&jW)glnfoK$z9S`aE`V z!oB8&lsXo|n5s_=5sNOaw6oo@5a>A3zo(cJ5^Vp6;7Qll%a(eM(j*h`Y~-QDTFbNUtKm7{XBKG%!4L# zjhjE}VNJ=!rH?A{6IYvRCU)JMhL>Bel&ru`Hwm4H)_vP1m$(YY!>HOKUTA$ZDRru&&J1YpvXxlE$vC8pys7W=Jk87uL=Z+oB8ZOoRw~-LTb@1!S-fuPvBioUH$0Iv zYLb<_Sca2ygVU>F&MY)es-w;|u>4d-`VYY|Tu&Ml zo(fuzgUTb5J3&wKtU}=28Jn3}T}<#I#3X_uig+(^Z#h}PapG8N3VeoJ*vBrZP`nAD z`S_~6{3_2}$@K8RBaRHP*%waP^>qlLHw09gsz+o0ep2dO6qT%&LVC!ZmE;T7t~N>v z0btdZx9`co(S^%w^iZXD{YdhfvJXzLDl^ip!Q@TUleJOWX;HA<*NFeL}43`5Q%Eo*#Wp> zk(C)p{^{A~&D=|ob>Xk#Lef0h(4#)rny*!bX$vD6qWHbRt@8vUYw8xZ(wvJoRN>o* zF=dh_DBbJpR8P8d)9l7T^X5z%TYjE*YB*)myBTMuqx8I*6PtJvT3Fg$^^irKm41LkOAUp!$vz$I zO*NG7!&VIy@&s;=Gp#LjFGYb&VeH6i=Y(-=;v%oqH8ouP^7Se>xuG<~s}{j_KfH62 z(ZhbOsBywsIkf3G7xtwzqF$0)P>-D~s<ITJ6c1h5INEKDv!Xdp#ZWblpo`r3Di4O(!_rx+{A z^kIUMoK@Qp_aZn(AWdKEA!WCZU;B3lVqXb?$mmR<_N2a8`{<*ytB@cWMcEkXwYt?kjQT+5 z*Wc3y5y8nlDUK{xs_>h@2#x}ichiEbI{%bWIEyp@)sKXaa2Bp9Q0>Y3N# z+AR{+)51c<@rX7jRm*tTxmjPyNy>}HMry>a370WCTM3HIoENpeIoezTIlbiL{-DpP ze@f8Yrrbez<;yMA_|jb?@qvY8U~ zdQWusF_iSTtSbuRa44y`%X7_ zR##{_e$47hNWiYMXuyPEl2N4fhxa9`XS;#-^@J_?jgfptlA>A4oVZPG+)YmIDEZWe1b*$_=yF2Uo zc;FY5hzDtB>QfbhJL|Pu^7N&(q{6Z|6NDq)t*YEdgk5KMn=DE&NI0J_G85y>p5L9j zv&%Y5tM6ykF8c+p0=f}lvt~^dEEM}8fh{e*N7WsSy@{@Mw1?}b^K#UjFPXH!jF{%z z==ZJK>+BAv`b+nnt58k2f%PWM+wo{J#j^Ve#)@Dc3pJ(*OBuwn_O33=8NRqjI}k-{ z{_K+5XDxfZ9d7SWVXmFkLGjK_@6dXrA8B{6Qbelc0$iX8=$V{mfvmuA>yTgzve=oq z^yz;4XH4+>@WFaHoE5UO@m*qL zA@g!m%ZFHR<=Qva(q`Ip${B-pwT1D)qKz?$q?6^;ZRdMQ3-r{nTx9hNnQXy&D=9HD zW*YKe)58K5H|4wZBO0Bgbi!QcJ&_=r4Om*ktj}Xwt%@!w;`Jx5`68V$oX-pCLIRLZ z;qYe^Rzh|4*6JXdPiie*+k36&=OB4q52bXQsHM2#k1Yhqg+RP{W~1So?`db@@BD}N zOoRB~pfi-bUi=m=Q3DCw3Qh`XQSO8*7t;^T!wBQ*FV{nubA=vBjYNGtF}b zr?dk@y-K`v*Ja+KXnZ8gZ+?mD{jQlwVO6ey+r8y)d^Aw%LKEVds#=J~Rdp!;(Nb!L z5)XOiXBhsd<3z>bqpOdCjMwAzI;IT6sM?K&VZHC{u+-`&ItwS*Ki4X&n;`oPx<+Y; z`o0T^ul2tdcL;N{gG#N1jw9S=g!APMr@>a^jmBDOg_;~)tkt`pr#alJsy_B?U~Oxv zrS9s97@i0nuozE`(nH_AJ7vD=F?w=6XTcwFlV93NX|u}lc|q={**S_5=?Bs_g4S^! z4v1V?HmwrdsYK=sxs+sTOafCyq({`v&gq4V^ln49y!ojZL2tYY#T#4PWMbv^c9mL5 ze4zQlTJ~ld^Bh?x=4;&CqQZBEzXO2 zCeOT?J=ymhlAdh&5(nk?@G)#mkRQ@aD+~vjeMvI3uc~kVhDc;*3K_42U3W-T)HtE; z&5ChzAQteg?SJMwpgMnIz?dv)s6bIh@mAB=Q;#h9&;_j0=nm`k1Y7^-#|1D61<&0= zZ^(+I$fLWptu!V+wVSdXds@vh?9mmv>s-TaBi!+k_0RE=NiFr;T|+Z0KlER8e+rAM%~+lX;=0;NlI z+#6ro0q;9Le$8FBK9=uE8b9Z$7gJpyF57bQ;D4c7-^R0Wj=7_fj&`y6DrM;$T%>_v zJRrya%S2%ps=9(_rFS`OVI_;LYBM)GOp~A0#s9M_8~hP-raEefzr^ZF&?3i(?*gUv z9gvM6*;D_wgb4vb)nI`{2WJK?1|$qADz25k8){nQV~^#UWTJN8bGr9(wguZitdqo? zcO~1Pq0NxP#2Lv`k^d!@>XMqvZGnK>3(l<{^kjH$ChCU;7_pjf=Y6?ipx$7J?`!^5tIl~py;gB!n3e@dVT=kt<@<8WLTd?XEm z=ij8%xf!bRsoy8SK~EdFE9)$s_R`I z$SI7DPkk;K*7oFl4&mOZN<&J0i1(f=t33jCY6O^YeBelWB!M zh`(@X7|A22(}zn!?)nx1mV9@jeO+$*n=5AR|x{90Z0=DjY)fy}87SM054 zbVqQM1rvKzqV7zv^e`AD1r46eBmn6eej zrshKu(lm8Rtb3@0$*`Ba%mS`sDlv}q`{Fq*wE7*#r~jaqLcd1nq2@%PI%qcR!V4S4 z(~~#o&pD5Gs`-Ts8xpN3S;DkYS5@+lcQ^9uyNDyCu?7Q9$bJ_{VeNCtMALS|-XluE zs*N-?+Cb>UYO)_@>Fva?bI>1b4HeoootN-vxtq zp9+Dh5cPfj{LHKQh%p+Zk^)J7ao0}tI6>>A^tA?h+-~Es ze+sJ0Gz2=3rm%Of{{2!*>6dXuv!-f;>l*yCVd!EDnXaI!ovHeSy9udeKCvyaS-TKQ z9$C$n?0gkaLDp^6u3-Pl_rmiUi0p5-j1x4#&zXg92}G`IY>#s1P(o9o2F*I!ihU}4 z<0_cgYs)_2gq+--YzAnL%EE+a4n}tQBjb6KEuqC>IE&0Fx|JwZTf)&#mR-hL2$kLr zM`Q%Q+N7QRttY}o(P1crV+?D;8awewou!ujNaqt1_pwmgid4x&Vs++Mdi58>bT1UQ zjDbgVd6Y9?ygg(6ZPZAq7OO7`ac025GgiZ^cH*1%t4uxBb2+f^617BnJA;|;7L?>2N6z;-AWV8Z|FJeP|pWfWFExW1Jpmj&^ zlZGKg2bLq%MsUaD9S2dwJ!KH)n)Y-Adnn%{zh$)?LlB$m#F5VXB?69ulYaWK+Z(Ga zVGIqD4q?1{A_@8t$VEkm-qP^Ad=B7MfaQW?+>z+j$L%79E)>VBgWWCJ0@lD>ruVV8 zzkkS4o2l=7r*C&7x2wAP$;8L^4z@j-N_^9-RT7`SWLFiec>Ty+Yr#Kun z`nCz{YOt484)V2Lyjl?C*BItm6SQ!yaK_uB!Cu3KTx2c6+cCUz%J^B+mRLKenBLYF zp<~D*s264whpdNo#cQXh)3Qcbeltw?$R{a+39yf}URmPS;EsBwPAo#$U89rUUDbTb z`qc~lQb7%MGf_WGe)To3EKvBM`|G-hN6Ec~7oBp>ew=vG{UWm~<8qk;J^Ff^nx7O0 zTg*a8YhiD0c&e(zBt$vPzn2Pb@}(Efm>lsiqBF_2`ICZb-{n4Kna(uF>9rjhccskB zY)~2f+nl!2PtP&FT$s&Lr}b(PLLImCg1Q6ef)T|Ku|5w)7IWXGUV^U^=RXnc(REEc zY<&Mx|28NljIQZf9fE>v)#0ew!jcqvMp(>Qt0__n@>z3TY*5dPf6~@xSl5sHj-KJD zm`}jhErMib{Y(u%&(ts}nc~RiWFt|86+7{0@nbzOuR^wbeXptcczveWan={`oV#r% zi+odF@|H8fn}}A}#~6-L-Q_&J$0O9;u*Q5`yxYKe; zv-i6)1k`mOxCSOlnGx1?8(YHgt}r}p@|iqqMq^ihWQ+`ncsd_3L3I~TscBF>}*9@P{SJqnAnw{HFO`A9s zMyt|AIg)(sLL*Zhq(yZ{N@MP#rm@WBPANTW4NpDB zG`rypQO=@d6-s~$V;%4hbK5!w!T_wMxocD^2yu=Dd``1(K zjD`G06v-*lVX&KymSX~ z_p!ltDfG#l@!K8QzU2&3$~rp8t6cbnq5ZeLaNcy)q4TW9X`B7?jhz zEMigJFNW)QPKZKdBLM>Zf`3-xp;O3cs<#t6&)3w1;IwI7s@WzvQh^J_3}DsuzV3)s zUO$fZ9kA+aLM14K@%z@js>To2v_rv{KSP@qGEU2QkHuRm%W$#_)HXhPg-q3x({cX@ zul`(_^+C5=C+uB@;)$mEjq#To7VEG0cyhPS#s`j-h_kW!v^8=Uf{T&sZ@Y|-Xyk&a zj#sTj#_+A>&hX9MdC0*Y)g@)XFs#bz0PRb*oNt6z(>(zTrnSEOoHub{dnOswsyUDs zaYiu|s%X!39GOAe!^oxHw4Tl$>Qf zSB%GDE%+VyY)kaC+gT?pwjB88O2mgPRMycJ9WVL(a}jnKtw-v?$|)rtwkb%$;JVni#)2Yhhu4%%Y-qhQ77qw~w5&AT{I5&5KA; zwA`p#7`o-6)D$v6dy2HqHF%U4x_&Mh1LJOx7#$Z}wjhW0UQ=I>rWsKU)!-xldVT~} z?(%hIO=S9*yS59|4>!R=!)Up}%SM0kv#4r&4F9Df>N@G4zJN>Kt z4B1MSsr3@B;G?=CC2)*ktEQDY=#l_^A%nLHxiF``WpFHgz{wo88isjIEzF^%b=iZYlIbV1b*b`T}EUDW(OU9S_vdM~!S=miXqX&+%HGVcj zj?^Jc_!J5vl9ih}IP__kZsMjm=%DA`^<=pQaZYSTg$gWke!$r$Z zI}|tH-~DOwmq{3%pg} zqJ;RF!w=3gkKa=BnG6(P3!`E~txPv)`mavg!MpXxmbM}p9+Qv5u9z3<73xXIKkhd- z*S~$ao+a5XAUz&BtyC0@|Imv8Z5A}<>CD#&+*=^^w|ZElxF+qy;OSpoXTmh?2W%%c z3SDt{wEl7A8d1g|xsS2(HmYkazx=BShpB@kYR)P4yL2?fxbGPR^+;P?JDra?z=ysdpN-X6$!xn5wKtM4l3kC@&mE*OLh;*MW(%Qw z!(YyyqenP-o@9fh3yoj7$v&K~O-Daz3d`cC$giTJe{EcMbUh-tCpa1<;)#3)kwmSR z6<{b)cJEF2icQu`1hi0!9%N5>Z3ZDp1<1+y(5lE7@d|Aew+C5tfod4A{R*Lp672Ki zGp=LEuOdTrjpR!dF`D*3OxGDqd!WzmgM}s87}!S(kW;=J1@na_lZL2@o}^dWMPvbx ziryEx%GU;IPaXFjG~xS7M&QsF)qVuUS(V?MKfh-LnxRJN`HYL@9rC|fuACvu|F%f_&dp@WgwWA4#^j+r z>d4@#g|@3nodarl;8h8){J?r%0ojZR)IMXH!C1_!xEZy8$0mbxi9C>=g$|JI+&rDIugtl^V47^vAQVX-dEE3Xit+M4Z?Y360gfZgkV zo{g7OInvq-q(pD*W+Cl;2~-ZmQHGpU=vOiYLhU9{E7V;7x^+IX$)rvfXhH-QNjuAZ zn@nOYQB(a8{(^(%NI!(TKpNqz%WE=tLC>w*MA-rrPy%{_b2P%Fn7EjO)@QBN!CY;g z1j=uL*s%7XYKQWoS&RMzh>g#gd!fS6$1X{rxlpYnh4mV{08!4 zZAu&Nks|a4^1}-3u)x3hQIl<3)SZERe|&SLbbi33*ao$LW*tKXqw{qOOnOUE-}O+* zC|hHc7b<@QLNKsin_rq`Vm1eRl(m^>VrAl>f@121J89-w>tAJf-RdlFCEXr;U!$kD z50VEl@6Wf(PfInq0Yz2!VLTx1GV?lUS}*eABi;91cT-J-p>_Sflv!7-hwDw$jYeC5 zf2feY^>~OF>it}kTtrV$ALMiXe4@#=AgMsjJH9kJw(26&OGpC%uj_0n?0X;*R}FXLru8!P%nFO6mgKJtnj&FV`X;w zGBeaAhkOC?g?!Y-s_pi5!Teoc6R0+-v*)`LDzyi4BOmEylA{GQ^Dn<@8gQKvd5kix zE|!idhWwv9?9{zL|>oeoS6e9im5vPc-Fj#F^a;*9Xb&F!tvq; zSv;@w0K&Fvkj$7j96e->`G!2oyMhWeZ5D}-w5+p~JnE%xd@O6aDuEE?YONe$D%oZd zpQBHACucsa5|(8O>Jkvm;%To7vqc&RyAAoArE7w_gWhoT=buMWX;VF+5}>+hGy%l7 zFt)p3Lspq<02fdKlYDqF?qJf^p z%&&xRMXxrT+DyE}Zb>hC5kgWnVfuQB_#6ERGtv_7rP_+{ZPU5pti{^0@D0<<;>v~E zLK}Cn3xUNF3#^6oXj7x&H}k~#;hm<>isk3y*&!L|l6Bb8C8kS7m9xYd z;f1ETMXfVpU&1|2S&PD^^gf4sF2IscP^0CnL|e%o@E$Z&bG-O4?p7m5eN1l_RZbDd z()-{KO>Py{PqD^^`FqUouG*l@oExI6`ZYk8_Rb|0Q35nG=vkE3Gru>!Rr z#d$BG-&iwl3QhthCo`NrKhEAWoyf4OA ze37(9LRPaGiHxs4$%MAuchyD~=}mRBXi^fqZk!-{ie5fEK&^fJbsO0aUk)41-8zU| z|6o(4BE#uBIVF*c!0(rmgzax8#D^c?_{PHKjoIm?57TgA>8>@}HAlIgigq$tUP1Jd zRaLvtpa!=SiwGJ6rfFB!>hP846+@q2D-%6kEzkQ9&0wv40O|Wl*Oc6dJi?FpZVAG; zAJIy-aldpT!8Ei;bH+WHKGxL7Ub+DJm{$^g9vC7m$18$9W*sGe`fbta890}0=mHZ! zDdL`iFG_@7d>s$GB=zJBYf^YL&}dFNaSwgM`trdjO#Cs>dksJ-Hmo5$h4O)ZgY_dz zrh8_V$PbniD?zq;*CE<^$G)X>vlF$G!$K%b#96(GpJ0mj_vYQ;x$6!8)S+ZisM0DPJa?eUa%@Q4An@m*fJzp_d+B zxG=->`rn&`|i?gLWYfd z^QIzymYX`SGkOEO1Rd(G$atepSWfIrwnK*pTDolxw^!)Kz~7-Cx-Vo9sJaypn~`A_ zD$=+I$+gYt5Nk~@kuHx4;*{Gsrt__oi#X7);^!YJdvVn4A2hx;e2y&ju)3%!ey$C8 z)&T$U&T9J6wFi#^9>-DdMPHJGMt9v!d!wXX21`tM{irA~Juc@=>a|>pLYP0ozpd+| zX0e)NcUzv-ygl&l$||)=Ww!z~@Yxnl$2g%jCPO5HE8d_1I*c?)LKuN6VG*$v*%Vz1 zI2u}ld;kvw4jhXjFlB#q-0gmyPN1sY@`;@Y&t(UPLT`54;05nXN&^3|i7>BrXP04v zq@V69c%pfGvhn`WU*Zp>5Ej*fJ#;PhByUyTQzI-P#s{Cx(!O9~fQaNrApeMLw@URl zwk4>F0{a!u+m(Mt{3p=~ka7t>2l$wasJWF8+mZd61d$(#sA#a74RrP+p%o5ii$jOf ze!B1#{1Q6U6>6qNGC)@XwuLTr{nmK^;3rVwF@`UE?~m9Z27_DEE@V8H?z(sMM=DIs zM#hBxWk)Kl!URNHjfINrAXNlJhhHBgPb!tl6~u-%lF`fEkmXlmxs?HB{P;m;rFQet zwpY}PX}s<^l0~3JSWWz%jFO%dQmf%sN$f^O3^dOh-|W%IGPK0~+KW)9cYM#!_f~yi01Zh~9d6CQ&WjUJ{Vu< z4HZTw+1Z-E(IKoS4kTlt7hi9ZyM41)Bl{P!&%65@08m(ldZM#LwB2fneaYzPc_0SH zEq?&(szW^$|3)CJuHC62AUX*QT5o#kOhb-$>L8agXx|(}vQk64Y&nTF?d$o2`2M!u z_fu;PR_+jykJz68Uv41um4D7fMMn31!h@k&&WTkOX*TYkf%RhK8#)#0agGt6QwQtV zEtlAk>^t2D@Pw?tYc{biSp(fi@KvZ>S9Gd~q8lD4qjLmr%;2 z!wC42N-}p{DsqD`iCB&-n(pqQP=Y6mqz{hE@2h!lLQfS8UpWR<3 zhKT^9Qt0>!4EGcjuDcS#i4mj`=P8$)~Fs$!sCJfy&G#^Zu6kl?3`Vv&SoJxOvQhwGYGxpH5HKL>TskN>n3G z=uz{2Rn_51XpY)@Rn$5CB;j)CD;~|uGFlD#UNL%7=26QHB32rm6)7T5{^1m1k{I%B zP!jR)j&=K-07Cm)tbJkJ?+z(zu;Dm0;FN@XKm5OOfTDE%1vt|PuV;To_Gf6#{;coU zyIbAILme#(zP zk96UuT)S+`QZLT{++q~ndG`yDU;A^ON_U|0&!pJ5>L3 zIzzc$(r5!GQA7v1g;_&@!s8B;(3kH$zrv|Dx;XF!=w0CG`m`vJn_|H8YlfBg@L8(i zyE$9xX^GEcJYh;P@kavJUY+InnbWUR2^0yliFNP^zt+@p7bJZukJyN;gYIA-awN4? zpmKAy-Rz%FrhnSX(l}?H$)Zk{T#^=iWz)|oq zYO~pY43e75RV;{)$B)FwBdER{9o9ho{~ku$3iNLM+IK^#G_eaBV#nds2+br%dt};p78L zzk%@bE40$HZ9J6{H(?q)cw$NVbB<=0N9_DdKme*^Js%#5k>Z(Dx4bDKwa=S!@^Emz z7CChRP($@AfVWz8K6{E%d9ad8GuZ2Iz+xXoU~KP%f@VK<3(og{pC#jqV>ou4QJNng zw5iCLsR}?ol|L&J6561Z&ZFo}E(pU%9v@DLhe3V=v`rHWv+Y13`#qlfaTkNV=Q$08 z6gc;)#Lq;KWKMkq*J_HL*u|rds6I|OdWR_5x3$H^OGr`$59IY@Xg^HLe>JpdA4}jH zY=2;V20r0Pcw|rl|6<<#HwPyzo{3=YC(Pt#vD(@b{@=aq}5TKhXF;tW^3hrU7CYrhY~L zhp`9Kk>RNc*tVv6spEc<=a2WZ|Cl`qImDoA;i5x+CeYcnZPbkWda{jHFd8y>lt;Yp6*7 z%k72F(R_q$Rw3;mndjj7b=k#7ymM38^-B4DHY0Dya=)k2 z`F*;Bfbdb_C*WWgaah}65(d&<+wTHcFG=5r3C&WA!Gt7x;RTJ74t#2H;Wb`tvHMe{ zCtDq{$z7U6{bvTTav(RgcduR7fnm%r*K(l^q`)c%_J1O)KkF1`JV6LI{_W^{pkPRw z0vU==+2Y`*M8CFx(uB)I`Ai=4-QUkRAbas;?XO&O3@~hVk$}YCc_*NI(U|jMpwReD z+4ZfNl+8%144W%70{uJ=Mw+HC=3cf#%lDwy9~^51isf(1<6yn?T1sbG>dEwB)cbA; zAvYiPvp>sFOg$(WLDBoA@7LnTNSRr(9?v3_#h1LF9{j3xfJ@D*kRiaYe=+!n2gr+m zI|DcO2Q5BgyZ@t1V(>>p6FVr?_8okQkKihe8hCwR%a4UG0n`;>63O4fFkEKR&n;>Z z_rHaCOQmPU1us)Mcg}neL8$IrfOObdQl)?&h z!=8XA2m2&aVfWXvP3t$?niPvOJ9WNnI+k*m2fcVNIB7mHasPxIl1~?QAM9y#rjsRw z4}DIaBUeu@JA@MjcFplM##G}AK9j4E5SFb(t3j7@Z@={WTp?UjALXEU{I1Mj{j?(ws z4~7@p?nd~wzeGtSJO5prx2-L!8Gi^Vm+3d zn;!N`aPpz&gQK?qgD~&rS+x(%!^{0T4ctr{0_+1sUFDEddiF^-;YD=rUBfqRN8^be zk$j|K;%ghYt4Y}L2;%dZaVQZ;ILpvhYi>=yC4^kx z)jVL5_|q_P2rAj;Rv2)%YJIM`KX{g!gGos+SI!`&0fM5e`x%=!_ox zxu8bNrRcmm#C+}9;~eDnYqRsKkAW}CEnmv=bA9)lB$$hB&JPvdZZyY%@#k}I8fgAh z0RRqLCxGh!xs(PMTkb`J)jRt!g7*jk?LZf%S*_|D-WsSZbEU|>0CFX~MSlo5kl5G$ z@a4XphqS-+996LTyuNz+*^2I(Dvg^i`UwrQ?j8ArGi&y@T3`S04L=5HlY0*E2~dN# z=u-59yWX#92sn-^9m_poaQYA^yKziu6*D8jeufgjT%(ADNt|hR*f$h0bvr`Ol`xlj zS5ND}J~z(@NmXZcSZ#V6N+*Z-g#C`}8!bMy_(QI3P~{LuZEF~9|yqrWJ>nX;5K zq;K9l0wyU)C#+Kr0&Xj&@^Isw=4}mVnQM1hhhkBwZ2{!a>C;e_!N*rFYidx-$=*`HDU;Tdo?bkL1={GUny zt+1S!eLHs9w-o=fc44%xpiX8zibk>K_^Vkr%x#@r+t1=<8#WB zdhBY7yvFCy`^z5p2!Ol=6Io1|> zR&N>cIrITHi55*Y`+G&ooC|s)2-na>PPdV@A}*n04eTEkuja_<#UN&_yFa_>)6}yE z+96+i0g?d6`0>NeDo_M=0LTd8xOCV89bb!uA#Z66G37gjZP3 z?yQI*i|fQJQb=2Sl6xsq=Umm3M;r~U;qq30CVP^^51trVOv51bh2*v3k(?uX1VA6r zO>b42B6cgqitN|A1dT_7iP)ijj_U}9OkK^!Z^7?4NR(;v*^LxCvXgY>8$Sm>;P6(W z$pZl3^{PGDzmf3R0STHQK9${~8`B6gyTI{EaW1<@H>*)Aq=xfQ(?RFGkawIUs$GtR zhQH}C-WPFThWAsNFYM10Q?r{83>vzX4X&SvZX9Bv*(Y>QH(dJo;3|m>O$7UGMNG!W zbCJO3%j_YY$ZHpbj^RQ4MrFNg5D^=6kKDOWFvjuu`)tbnfkbR1GF?0E3+;TA4fZlc z-Tr|mkKI#zn++GG_(6Ga)mx4xh8>UoP=*ImfnPGX_YpsSU-2&;zX3>Lgqjtg{9^s* zfCrbo#b|ul=@fag4Rl(+9}OV7~YCobPXLL z{fQ%&yhUi-*ohTMv(M`wg$IP7#FUB`vJZx?Mc6zb^e6$&1@Va6cS+eno@_&%_U{P+ z(wD=SqJT_WyJwvQ=~>vf^GmX1^9py|)lQ8I`;M9P$*z zquNXkZ?>%$&ObtZ%BQ~gKz#VBzT0==0J2N;_aX0%DSIzuivS9L0_436*?eP>nvWg_ z9v=N1d~iN~oV$O4=y50?&%cbL>?O^n)w%p#?1S$GdXWLzJ6G%;t$ZB2R<974$RaBN z4}4ER{0F5Bdaw=puJ-|XAzTFFX8Z#)idw80q;TR>o63h^#PeP@SG1k|CTAVu`1;WX1e9Wqn7~^5@uJO#E2mi9=zv zzBcYDa9)U+@jy>WmYp`HcEZ<}o}L$CYTV!RHH$zoJU@T4u6V*1Ngocd=DI}?lRLI0 z;)z>$Q{bRbR!=gf1A`rRAM=YG`C%(w-!EJ7`hfcaly`r3=N}ZzyO`hX{f9B;+M~5_ zE>e?@o~{vJI(jg~$he~?DeJWMUTdt$xysyjD;(tEcpt za2AM!abOQimc;MCdpzZ$mT0y6d$QB?mmv3z-}g{v{R#YEDRZ07%?bZK{!pNR=u?XS zQgKMJK~jV>Lxhc;dv-E6H8E8a9)AJAd;9*g?|{6%@o(e)5L8DIP6q)S8})Q#CTl8G zCAdqI{Q<2&;(Q$w=wX zLv9*t^i*WZYsP*Pdxu~8XCXI?pY^0>Dr)9@(|did#2vK(AMqj+ET(O=?uOANyDi0%|+?V=ONUsK#bQO4LQlqNU-Hq%x zJvoHI_;%0BOivA}N>*3tW8o(tr{~~r;KUFzW4fLTnIamN6%((K1mT2`*l-uI6547?=So};vG=SXnCy**&f{vcmulBJ(N+Y?p99xJJ3wJI+juY zH~NDH+XQ1~L4-o9(1~tDMwmLNT+bP~gGc`Wwh(tIcK?C?0RX0m{`=scG{BUW|JH%0 z%?_8ZXLqc6xKbq}N=3;wc8kvQ-O&`0Vk42!XpPx?bCn~FZ*x4|7H>< zLz>~=V;u7<#>Zz$1@0~$DJ5JbZ`p5Dt&legurEfJxUG;k2wXjE6a$A%?2DNthrNf5 zAH9b(yy5t)UN%<;Vr=&tz>b-FpwjmED5Hn z5IpTr#ZO&V2O?uq>i*E>^UIQ-wEw$F`an5dCa)DZzo=2-_M;)XOkN{Ey(m|5*gy|3a?WJ@juw~|Cyk%|HY!F|G}dFUj;So zK&gJuw13Z~WZJ2}Q}Q2a61n-U<>z$$Z2UcM9sf&|kpDxJ{ue<_JDXRks&Z5O7Y;?SUP27b?e}D?_&S1d%t+0W#rPDR@3%3z>ghy( znJAu-@kVp&UE5ZF|4~t~Oh;yjc6n;#Hhd(3C~m6@b+gfMqc;OwU~cPBSoN;6wpyH* zC2A>LPNdHGwWbj(RN@qottgCxl|ymiDZ=Nr#+`xDz}AXS46+P_*T5r1>&nWLjNaKq zb{5;W<&y#*^@4+Sqz#x<$qDJ{q!0W-o3h`mhkp_e|FlE%L|y%Gx}H;CR4a}%#{Sbp zPfAt$U`6YCbLG=s_?w%t$aC2$-$bV-c~XHVh0}862O;p!q_uALH@NR>@d&7>JCC3( zEUB#jAz3JVqO`~ja1p*ps9XFWl7+7MS4f2f|4A0Ah~oZoQ_+lD^j!VbIf4v9c86~$ zY+1Z+uTepd5ht}R7HQjX7_fIr?j5_pl0#2y8;@LKAMrsHXVe51&XG3`en1U_)?*S+ zv@H#LEx0zj##==fi##QrFyoN=S?r!gFFNKxSi+)Pe5=707RGdxIMg>OMVb4H9#R9J zL!%Z~u`TdUtZ~IZkH(BP2hVz1!{DFz4(=MO?~;)5okCi=qsP#;_uB|EO%iK)jIVPcl zu28szj?XsX^(s|!84-o1>6U)32X4#d+1QWZTr)*lhS%t0m=gTg=Z`z}|1F$DaRV04 zp^J2mXnOXn$c?I9dg7mFUC;aYI4e)+|nDP3EoT(LBsYzM;N z`9G>T)c>z)4o&R!l@@)Jj~u^gXpNZ)(UR=RIqYt>?pXIvZ}Ml@Enn~Lm}wG(uN8CT zZeN@Ad$?R2ibRHdks2>7D^8(pT7AU%8-1YuZ)c*AE5O+nfPml#`~R`!I=TKYw%p`w z+Zu|Y_@nJ@IfaBK&A%4=$LOWrp~m7<6B~BXi<0f9_kSLp z^KOOOI)pDdjx@Bi%uIXXR5_EkiMen4pUvA|PctOmo$nt7=zMQa4d^Y~Zy&KFZ!^H2 zCy*SmPrdiY)c(Ur39hgA_1W_TFmOee*M?!s%Gd3^N4L}KndkH4VdGM_>*?lsV?}Nw zl|h%M+oQufecSzV7f?jf<#GKWqVv2l(!<;B^$yrycz8Jn0{P9Ddp@uCH`sOgK0()? z77s(`YPL5sC6_w<&thzy-Zw90Tc0@ccCYbygGC+hT1INlB>3Fk&wvY&4Bay$Z>O2L?@Zfk&HOyR zFD<}CT!zn=yS)wI@!y9l;KRfH!&t0skNf+X&j(?7DnqxM$HyIc!NdK+{{F+!{fe#c zXw19C>-GKPM(T{NwOp)jmz&bmu|Ovvr|0DiwTLIF4#FAo@1>TN$x^fDu9tAz*qKIUEj|ejpn#*z{h>T z%;#aO?~9k`=RUvf=gUV)ZujT)vG0e954#u_|I~9uijLQ-cE*m{U%sV0hu^bd*}w@-EwFj8hdr7wLS=Z?XQzH6o%mmg234<8R>uem-SbMU?|@2|Nwxo@Wn zavxw8;!Eod)7aR{khH!xvA{PHp*Lr?XW%*ESPk(upYQdJ4DU;>qsO)pysl4^_wAc) zEa#LiU$=PVdd@M{GicUd?#e1Zy1qQ))hT-#c9{tAPBr zhPE3IB{MhKpJzS5fmq+y&l6>zn>bY8rxJ9&Pc_xS9^cQWvk8`oV=%}olT+Y=+`ZoK zUN(S{xW2s2Bs%=>1Swbh56`!_U|8L6FF+oCcmGHNeE#UU&oG^!?eV=oj7)tV`n;an zXxV5=bfX9zu zd4%`&o)V=O7SFWUx7-@fWLy(lpX&uf5b*f9CimULw+1LyRZG$%CH8zn0suYZ(#86` zv0Fv+b#3bOjNiW={i?Xwxa@f|d8O>i&Y^gpE$Ml`+wF6Io&clcpa6aA^Ig~Hb6aOi2Yaz zbyql81ynf#j%z-BmTFpZ`3m{1XEZNf8(c1q8!qmyE2rjiibi5gpHGt+=AKuct)iQc z;VlmzA3m(Ie)VKAKUI$8=-PN?FgKX^;#m^9&*9C8=B{Z@yL`0WplxK$%(setR2(L& zZB?|S&1mi~+_vUKbF?}0=SH_3r_OMe1IUTL0mc~)T11$re4I%I7cVBfQ!Yshn^e4a_Y#tr8|8JuX(OI{tf3AoYCIZX}rbK~%*yc^bQEuH!g zu1AmSQP~VLru(D21l-vmZzepoCQl&x8fuRFt^MuaI)}nz1ICy8sK3X7$MW|aT)gA7 zhKH$+hs+MoI_EAUF+|M-ug`I@bvgI%qaiWb&!ZD9%x|h&wf2W1kAEMibGPD6zJHw= zZN`ZW28GaUKjy^j_QUJ6@kCtwO>JJ6(vL4jBpodrL7nX8Jh>*aniHHeZ5?Ub87&+R zTkGR3kF*^-rOku|-N!=363<@T3pq#jzogmuM|BQ~Sn85}k5bWX1`NB;FV{($X)`B7 z?@k+##rSmPwg*VTKO=)TWR5vE%aO7y?Y;+Tk9ByrQic;7Q3V%ddtRgY#@Yw!56RIj z@#OW{89yI9=UU#!m(fJIsOYH-8S^Xq$W3djhMyipA3K0LZT%agYvX<63}ze5HIwnp z(S(>6R7AL_<^cClYZ$ivSp!o}nD|GoTmM*{_byGILR7SnuQiOgQa<*sszQdtry~*_ zr>66`dP(nlA#0-R_zz+4%{G@7M7re1)|_<@C%v`mli4=boY7T9`iGNln4(#M@yODd z<6=l&6H(aw3C!7D)h@B`WF%QG+zlI!UJK$keHt83ty%$3cxsD$PmObego-|GvE-Mm zZj(c4k2)?c*~dbBQteDUT0Xf;y$NQa#9@LleNo2;hg@UT7FM`Y35Tg{)5cMXFZu0S$UyLh53_FF91$5n`NmZfY`@t{vpZz)7?tY$LU%zI3&txZSn+;#;&n z4mV3;uNG7uq>8x6cj+(4HLb7{$`t_;h%k&(n-HFJnNx19d|D zfEzT^rjGBg++GBsjCRnRyx4zbj+sxpXT*fe{oE}-ng}_wW}LK9zJoOmb0s-mz#IhLq`>B_`SJ? zuBOaamRO%w|78>TGOc(r3~!hUM8nJ_Qc(GTDfee1zR3`cZ>7UCC9IlK+Ke>Z{1EzcPU? z3zXxrmzH59|MT;_za}s#Oba@6Bl9ClWx)~QM%TFKISJi7s~YZ~3S;+zad2e6HRRWp zi854eAz9*+s%)5XiOsD6%J)2#j&P~8E*7FU#D-*{l-S|=GrD{r1fkR3BPXMM++m-` z-{-q3%&+OUaNh`Sbw-G^66W}R1sTISi3fdS$#HbwTf;KYlSS$5Bi`2-aJGkpCW6O( zhfTm=ve`>`OJRoQkf4f3CWD8@;T)Q!rJ`Xl9SaCS$fu$=hnUvtuhjV? zLLxmb81*AhWM@8_a(ANF)Grbt#1kW@;|bPO+n(B#${ul6zrzVc8oMUJUaP|6^#=<= z$c`c|795Fs7vG>!o;yIfLoqu|sfk)6rZ(WXJxNBEdD`Wf#@O?eG$hT>~M0zCHy2 zPk$_8Jba!^5C0Fm5JaA#>@{2|T{JZ}0^=5IbL8K|93o*Tl-I0Bjvw?AaeYouM9Yh9 z#r`#?JjVy&NlYRW%e7vFil(TOWHH=VlA&s5o~|8&#CEGvo!F!#jj7A9C;5RRgNP*b ziad1CGmLCrlMpae0qcE(!i5Sx-dd9zL-mrl>9@s?rg)J1YFmsY@C0OPMyv-n+-`+K z3wmQ|9De+tKd5Ug*h@Ixp%JY7?{Lv@v$*;Q3JXY0AsL8~*%_&zOibx$1F$&e10wx*Nv-|joDBa#wDLC;iW4z*`_l8)sRBrT(_{5C`7 zcTdjV4=F|qN9?lSwr8|`+TPcT5Fv&dt#*VT*bjf4m>j*_RhH8_CHprZvQMQZ&an9| zqctzywt}H|wboxOd)rk6k8t6@h=+gcLK$(jI)*{@Q=hL`-Vf%neM1Hu4aPNLXbAx| z3Fw#?a-c7w{Chl@`OpUoqIhmA=r#mNONByUuX&W#Vj5p*SG-?|LD)4s*7@cVr!c?7 zV&yrF9U+=_8`82*yV3DMNh8%`W7H1^t?Y6jC5ohm9$98cOca6anbj(k1`aT5j;i8Z zBv<-+@(L~~`13;5%7pxW2%U-EeMEbPS%4z_zB86zfkSQRm=QJzmpsyjz4r?CCqWFS z(sJkX-;L6hdIb>rrP1LusB1H~=|=n)O*pU6GHAasx_<_x)+9sia z^MI7D1{w)D+1Bziid_?JwlPMXJoUt`mqa(O-;%fs;#jU*K2-Wfdt+sWFgL$1;PD~Z z64IOWC*N=!gq(PUJ^2XJv$mNR{I0nQhS>`ag&v2Q#b|ZEF}RSRK`+)?`!M3s6i*f+ z45uIAfH5&-Dy8Yf>L1Mo*Yt_*UIwCU@L4O4|+~oMU zV`FF>Kq0CbyS)UnVNXOnr4Wp9?fUJhy#n1ld8%b7o{nD*+(>$0hMc(YgeJwF5Czb2OL=3}tFt@%pd*`!gm~CFmB)dE23c5jmG!=8f1c&YkffE-@ zA0^I{GhMpibP1&K+z%XJ95+RDy3zHsmI(~5Px}QKR;qO3{RD-6STLH7yqokjmXZMc zF%#tNvEq5}9IJ?&%mzWL;Z4h)?}9TBon;!AQDT^Q-rx_sk#Ai*0McLzi=}rvJx zf0MEtcoUYFLcp)Jhcy}0%asp}Mf+6P7^yo#WD+5Av++}e!iY9&e} zys!s5PDuCz>J%I@3aV=>P$ty>Tl}3=c`|xW_54x0kS>KL(frpRol;{W4}tn-JJUQ&9!6EzP*U< zYO}C;&V>y39Q%L|R*CK&h5l`=j@cVUnjiH`(vYcEOE#-L;%;vLVhC#g(i}(Sbbb|A zw;K`-3Ehi=6rN%f!j}KfNb|{5P26ZMH2Lan6t3y{x9zh(t^&XmdUzZACvo|mLet=I z$nYfJQRWJ5`>~(=<_f0=!A%refn`RUOw>>;8nlq?sEU~dCbQq?_B`E+CR)>{eG|~r zJ@GoFKSvu7nsEmYgMI|jtktf|Y_nEXF#uqnq27t;7gTPSu~VZa+A~OTpic6}2ENFM zN-@AYhYL=xAFiO|vHPD|w2P~wKb5wLHc1hgY94ZUXF~Lq(*+btaO{(yiK%D?{e&jd zCes;V#TOJBzqbKL1P^V>zl&qD5lY5`m&>E1XG-&Oc`bN85@Lq0cV5h7xnH*DH<)dC zwvB(mDaWekB|5l9;AIkH9~osO%M0wdB(&PBrMok?SRch2_Yif%c-s2KNZ1=qX=ZTf zOZh3+(WfGk&xfbr`otn?W2nfmVf3=tcELs64HUv~((#i7s4z=%Xq<60;KxUh>JzrJ z7X~ec*Z8jm8+(89l!No~_oP(Ee`cspTeZW4QgpfPi4><0wnT6iiQ>x}UzV@6i}xDp zx8KBK7|fbsV8|-%8Lv#`a|$a6EIaf+2+R*nFoXZ;2bxT4ai z_$~Qx{avR4wT7!etfa)$$P@XK^qoT;z#sa%06whzq{6n>1XbXJev@u1@;ip-XXiw+ z-5tHquQR&k)O!h|Ik>R=Xa?Ij`Q6~Mkm`%J3|GkzZ-O$iV#-t{0Y*T3IXvavBDQYt z?nbWh%YCiF8ooTEwn@%~mtn9~su1#P{oi9_MYDpxXPgxIebw@!0B|*X|N7!ZA}4mq zX3K9`Pm&Ui8LB>cU#|>=`v!3WO}TQ|F#ejy^Wno^=d>j6Y+DU6@hw=NC`Q~$vT zYA-^{K{TG~3j;)KKGh7+U^40_ENAPa9(Y9>>o+ti`8};<$Q(&okX2a0gAiA$?};6v zPyNfkKr{EuDP&D#GDEUh-e{Hx0t`NMz3D{40d z+yBkns(vEHh*sO1%M|ccT3i;Hw~yeX)N;AWc zMh8QasGY)LOBCmB9M%GGE6 z5k<2n>qm(=6f&!alDox}TSVquD3tICjzVJ)8qS;g=LR-<0pm%x^|oQlvX>BHY0EDd zh+%1&bw68iWviS>l>p~UweyoJWT8@Gk>2)hLV(e#@v?Jp^dsLWv&C}#Ln&Qv%oQkYWXN;=F zpU=!FL(bpu#?ShxK15wiLkMCp>BzE~d@kn*0y*bvO+@CoRIW>UDwy*eBv39vBgt?m=)8R4MI;EaaM4yhz&o*X)EWt?h=oL2?t*xasy zO;6oEFQCebZTy@&56)boM$GE9HU$0wq~Yz4vQ6_nt?`}+hq4nyaw~bHqZWO!jHn3@ z_WP5`;6WXM~J0%ErjBeZeajidk9@lqWDMg}&t}2DpLITld zKNl0m1mg=(qw3?wocj%YcGlrmGVh38JFY0IQog;VtFR|avY?&MQVSh28&Vtb*o_iN z+9k;oe$_wWmTP#gXLhPdG>|MnSn2hK23n;?=@cL-izO7azWY`2CeL2Xa} zF6!eSCdJEwHbh^0zq3AD>^47pugbJ%ou@cAIMlk)Rhp?ITbT{V!kkC7i-u^LOS71a zkoi@s+OYLbsAs<`O;D!OZp}En|%MFBuLy{@?2HA^w`l_(Vm+#qT9?~N9 zDMzW1w>G%lFptsokpi%~4hR%o2&RWQ2+l~WTNr=K3Wa-w$V~cbQB-wui;%fyyU>KA z3^^WXAnsNMQ)Tm!i9H0iP`*ADJ}vcTBU5Y!aQzLHCtBY-aqTEMmr)C0kbe#gRORfm ziJ(v#T_?t94Se~VXrOdJRb9fd<)^t%^Zw!wYRVV*mbw}sIY%LDEC|IP0_AzKL`Y*h zun=?`qM7N{eGX(S?eOu+^VB|L4e|LBpZ$gHN4k<0)=+e>5peuRljek1aha+Y5qzBR zAt1DUg)-WP?)NJE+721B@HKqDm4ssbTT110*&S-nNHOLpP*Aq5G;Y;~s$08V=qvoT z;>g+o7x2_SGAPC}w*chp;l68P4xf zZO4NYD3-2iOd36)v$;TmoF#f^LX6*1-!4yh!x6|phMS9x70Fb-iDby9#KqESPzp_L zZ-0mOp#kSO*3OBo+Gv6gLMq{DNqsvyUdBWQY_5Z(vJRze+aqw@$nZwV+g1gnKZ^OR2HyzKlTwXqTwsD!3s~a^iTNERhHdTj7yt zcIEekTDj?l&g6<-u3nBoRTKP9=NzGz0`!D2&}J^0^UxbCrz-qI5b7gzE5U!vv$56& z_c3B{tNCWTTy95~?jYz*JcJ>4l2Afv^mXM6uC0HTJ9qU_)5Jy2JC4Ps@RoYAUi2f< zt$8$yv#e$eW91B1XU}&fe3KHYi zmO73GAG1a!?r3pFh?wrNO$M8dQRP^>32PKgO5H!Y3gsTEFK_{thO`=?h(Ig5Z&ng# z3myDGWtj+AUzF^3gNPjdgq~UGF zD+=(18G=BNYQ&@P!%hRSt;nI9mWE>EFpx1W8_?}5UlVFI$+zm#bvlrXBNw$AqF7kw zjnOy&Y=Ttc{GqdUE287Xvt84wO9X`!UZ0KA1W4iBYKk6#_tyPE=E4ia>AqmttD|+Nv>`DlQ`XJ6ovOW-B&Mhy4y{_N{h&K@zEK2q+SCtC!)(BT%Wx-qk* z?brTjwB#P56CL=sOT{0D4^8b@awxGayAGNy3*QJ7@ZM@=ncE^2ifk%Uj>=zV!FKL& z#~7OU<5EBCPp7dcxwrad;zbLmE7h3qAYAD1XZoDt`vql`{l)+pJ>=x(?JgYVk}Zs9 z1S+*X0aw+QjFw<94#DtY_16R;*J#O>%b^A06CEl~zLX7>Up&MXOC^XHmTZk2DU8=L z`B9iSkSNj&5+U4giz!?Cy*^nDSrz{9+pL>IdXM&me)u=>!!fynaRj)*oJx+0;3M%a zJ6kni1@~+hy~um&ZSh{mhW7@nll1A&VM4w>tbXgQSsF9Uo84s(bvYJ^f+Z^b;g)iq!JqcD!nFkn3@VgC4-+R z4Cc10KyxqkbS7$xVW3SogDGEd`$L+9lrRyb9!=V-+X+1*^7w>}qWX%A?o@N$2pTL@ zN9%wkdeLMe--AWk&R>);Z!#Lv7vR>S#AD+yT;_WO59%OymG697lWO$ct^ViPpJHg` zT=4mcYt+Dy-me|* zN#$=Do4TQw%PDsMJ7fCJm@4upkRjjL8RcUWQsxZ`ZCAU0NFounMuRHuMQ59k(hiyK zdTH7Uv%q+vas1a=(1z>R;Wh)Ff*$TASMUs|+oYMX&YBj;?i7o4Au&9pCx3C$pPQYu zC{v;h(+;{8d6bmN53NpZMP$=Cd4OB06PatvJ(E)YfFK2&2-F!O1g$14BC4D((~?ZQ zb6g-=mUES|yM`_U<&fIX<#PO2YcrXMXr{|b{55CYqREh5YuKG@KqOrE-1*9AT?(Ue z|C}3Y{RW!y+>yHa45oObawgsj7T_$p@;$Q?ASUKH-eO*35u2l}-vcL&xQuCv#L>9> z!>WsaXQk8{`V?J20%*UxoIp5Bw`2KXvl(k-PVGCM3Fy;r#!ka+`Lz4ctMNIH0sys5 zG2jdfi9Nd%@Uyrv<05<1-VO5sVnY1dbXjZf`*Xw62l`Jr>>%fsj?rydI^8_jrfy`e zMqVYdxL5P_EoFE|wJ|n6fqLH35R{XpXOewCkTZ=)g$K6^5VmAv%$&qLJTRDs_O=0gmcJ6!!=(UY2fErwOJ z)V#-mVURBwExx|=CwW_cx7BbJkX_EEKreS-mRa3W77Poq^DTrxwmK7>Uabr2Wdp;p zlh+gY>Uu<+5;|wMebfhb4pY1d8M*3tdgJhS%p*?JIqG zC;@#?ob1RpDv|y2;=OF*jpj%Ae>gCRusVnBY$N>a#c!9db5C@R8y5|(w7zo#SFCWY zhI$VAc3m@63|k1W9EU;iWHjX2vp46*Hbi2Ne=MQ~?G-*Pu8z7KM4FH*BM}apSvRZl?drUsfqI`)>ZO6FbJ(=`DYxU;`}0U}ky4Md!ZhJLOGlBRJwphV{h& z$VN0wjxotq*H}H7=-mfBa|gpgP4U*~7Gj*d!n6s>qj6<SuJ_(Md%S;5$a&tZ{QbZ## z-OZV`mGbj+vgD_PKj%VT&(f?4e`PkgJ;n(nOT#9D&^JPy<4axSoZNEec3)r!#PXfK zR)0|=Y%tBBbFQSP6`$7tr9BhT>qEE#k$J&RG9FD3Q|2BKb;h2QiyC4G!J2_cU=fJ6 zw3>VyR#iyi>{JzU2x4%~0-ceigUit`M6(kK@%AFSGiWU{r(%_rsHyys=~_$CiwaMV z!jJ2}3^Pjg4#{!Yut4vC{uPNZ;M>PA#~wjHWjz5RQ*bkz?6CR# z&x6HbA6dWr0PS)Bej4YwtlS=A?BG!(@x;RX8Z+k1#2UCNjO8*t5Pob=xqq9G!TYMj z6BlVT!4t_!nqYSe1eJ+~#Q`$4M{nw>EhDWELPl(y=ra_>!spWz~Yms7uG8>Co`z54|_)f9xLDnAC6uh`cXOP4O~4+>q=X!DZv$xp?{wS zl7QFydo1aTKZtW^kFe>;4ZV6tne_*U8WahN#6ks33P}`B9on``zTH_0D`mq7$0V3H<^JmeUwwF(Pa-yYOWP^zq zH56}*0*IVbe^XA;p1qc2W;9xZb`^Lp>r`)YR*i8fgW!Xo7)+4i%4Ew(U*zGnE7%qc zUe$KNo7$=5y%Z#DpCmn{hqj7Ye?bN|n@1Q%Pjgw3rbwzv-VU2HZ}#pSU4xIyV$L++ovjMh3f6jdIB~-i<*Vdm&2c&58r)=eIw6F$VYyV&PcXgS7?13aHD~ zz@del6P=a%#7q>>277JCA~uN3z?d`N4}cGWXwk2bJDM7=C?o+&rf zM{>2c1cUPI4bW5Pm2=Yzr6Ua5!o#D4S9(C#_gbtMY7B5O>Dtt;h~HP^oKJte`Dv|U zg)+&ocT?9mXF9F~&=41R9$s-3k2v-S;M4qD@)@WMaaL`=x6n~|*#F{XnASQ;`Q?Q4_{VKNT zE)53xmwAhl1T|(Ty+3QvuSMC^jF|-Kb2iopRl~Cz&bEfd1C5(*+5pF8f*CND=LXhyzoN2{#!}BI)xkw+7r;2XhlM+ zPOZp9UZO$QgwRJOL;vM)vUKJTT!UlS*a($*B@_Rc1247!6G)ve$nMf+>}$BzL1!wl zd-`CAM0XaEp`C?x*!99r{G&5cO3eFx!KG}s05CmT{!Rk(N+jvNd0drsqSJ8&U9G=# zMA}|6NXUALv{b=GD~=s`dSOER<5bB?1dvh%7c_K(4sQ5&W|2dLDBwsilJtU$29(`=k55u2W5BfXLxj#Tpw1KRWAwN{#Z7?Wa8D#~eR~)Ktu%id>CVP&BitT>g0i z)H9GHeEawqW?~>&oo=_*)5*D<8-gVH@!VUy6Sf}neU6oZ@(&ox_7L=deNNZDCJ+=R z6mi~9RwP?gcy=$2ck?ThN*xLi4LgYED>&6O$&e!6ssq|6Ukgu5nMx#j{D}w|w;2~5 zr#-EvCU324n0Dt{>Qday09fPGi+?%J;dfb`6>O>e$t{L5ylYQ+GuSP5rBj{T0ic)} zDwQQcC@^j0d9?KSM~i)^LsxoJM2(q!g%FN7s^%Oi+T(r;HXi3f#!M?h{j&Fds%g+g z3}^Rf!Ij>#38xfV(k|;G zqbrKg{>X3C9tWaa_;7MMyM6wnLYfKzzj@5@d;2$?k~n3iSEARQe=U*wn z#$ol0*F04F+*(g5Kt!O9hf^dc2s-qIBc?<9VPCx+z%^8b57wa@4(1nZpT#>W{bJ_jK8husCLih zyYS$2bUnnF^KH7ZzKJHl;H*4JB~NxndyqQ1tF|F22GO+AtGblOGT#$uQ;EXf^Rm-T zy)|&tksIP2_=+XmdM)==B@$its^<%PYP%Z#me7dY9>S4MoMVH_oh1Yec|_I)-H}`m zos^+@SU5jnjRMf4`WsmT%lcvwZ4{z}g|f^5FKNtvjw4 zfoo&3KaolwHYMg!eGA$r`ibpRU<4pOaxKp&X}kr524S;Xep6@w61OovtqbQYoqgsfqK#f$cPuWf6UfZ29RhYqTq1c+7uWte=sqDV8=}!a zk6gS1Ey5E@*60n+?r&`RlefVsE&ex!CtK1<7(=xk4(_G<)tD>pw8((-s}nFC+m$i| z^^9{{VDK-tAKZx<&+S>SM?LC?I8jwbTC8-qpY7nQe66-e7|px$;Y$CVZ zgfk~zBElH2ybl36gFpm#W||GKv7N~>@|ZyTaAJD<(&8%!B3yWk7{P~9p@txh&f_!C zNZIWzPsQTEiTjZxzIxbzKFrm3%d?n-jnC_UIG~Qj^q#F+HA2lZoz#jE?T=8g8gkVu ztH~EX7R*->$qvCcmOs}pM=hGhtaLulHmTnIYTDCA=(dwa9_(2i{4HA7(%FTlr^DFkaFIKn3O8Uxdp$=qog~2i zCMlr2vY2n3vgFK}C-bMdgvmwCeRr`4v{W$2iwisKluxb+5)Ft?xA~>KAvgUy_J+Lr zn@Ny(3y-dotJD)k&=5lJ>v{WcFO*?JvUr4Vmf4GwDDzrBdd@+8i?nRnZeZA+9_<0M# z-ud%)O*>0f_8t^i^W3u%qPRELfX#SX-x#9P(cR3u1B?WgXZZ0~z`mUunXpEFG_?-z!Y6t5UrP(4Uzahian0$;x%GCVH;6`+*h;x)| zpVD&&APM2mX;Ff6k>K!+T{qtm-{#hjEOXfU*P0ObdG9h{xI!Cz8iccEnU~VwH(~!Q z!T7>Wm6tNmg>0d_*=VYvj87lzHe=Tzsn$n%euoYkco_LrN=TDxu(BbnSjS^9W_?V| zbo_IO*vGH1EuZ7^qFNV6e5J})&(i7zex@D+yZs;-X0kQF8CwA_9i;F^h|Kby3|&xE znH``WTZ31$qPL+x{LFhjrzo)F!o?Q$B6g zkHyLS^goXnPy@4Pgnif~dyrb`nX+UTq72@wuTsRCkp5Dyb8Q4J*GBs2Y1}Jqzk?vDux1)X&JwY{;`bSq_#5%*=HU$De}_lwGSfT`y-ES?G`(-Ci|&2p zxxWmuB%|#V2NlIx;y4Q=uTO7w|0@pxL70uIC~)aFJTE7|I-!o%2+xDMl z_Sqz_>JSz!o%|yLB)fR-@Kj*mZ4j0hRdCxXppJ5>B+;SZ07wPiz00w8X<_*b9zZ82aFW$F{^xdl3VJ%P^dv{gDPN2X&aJ(ZmE1Md(0j_OanQWCh zgyXa7ez>$8vYw;NdL;bbg||^oV$uKhz7#no?rD@LJl^5L+asLo^fY<$i+3{FvrkQ~ zu)pqnZIr?eLJD@hVfoN^?J7SLvYWmhp~pTMavg%VsMFP0 z0<%7C>EY(K?S#2Wm^^@s(VLDpeKxp2z>|&rqn7)S+T|N~Ge8-taD2gyM~QUbLD4L= z82|?$(sbYRfKwz>L(u!uQ`dK5v?*KN_%~86oXkk{wiK5gm@dI& zCgu25WE&VD1rwQIL)oBIlr-czbMNfIQpuHyv|_i$!CZ=a{^5YlIyk@P2LmSIO?r>8 z9t2y69&O0c8_3Xb_69ez}&waz4lVnt_==BFuxkxEKb*B0WUkF({uf8lL@SHN?YiJT!Ho zRDZ>TJm7#Ugd=DJS(#7J+y_dP08I8@sR*k_NY6~3i!7#uYMh9p%=tISx5*-PdxUJ` z2*y;I=nBb(aOw>UGyAG4pCg?DbqaAB@KO5->%GnKyE;Ss!6nm*&*bPWs`@s?t@ z1`&LZMM1B+a4uqCn{Eu^A7yq&?^Jq&{`yiI{DL3IyIZ`phVxOF$?q?$mPs{%2L&vG z4|w%w2*f3Q?;L^D3b3*~4`#n$xhipw;{CX|3BjRK{^x$v{1lA8E8SBn2;I}T*Y|Eb zo$K|~GEzda?Q^@gq}#$ktlRzJPjc=(RWo73pws2;<>7U^oZI$(b`9K5{TMuP-RAdx ze3>#dDL>nx!w!0XI9ATW@qG{5aOQ=8mh_Q4^aNpva7=J5XBPS7;)%{Y1^;o=JBJU% zyod@dDTKVV)7w=@_8ZqzGOGPanKu_WP!<5F=xfe38=8cV(9m%Uoq+cy(Bf>xS`3e) zE9Hib^7YdQKr*Ra`5t!;Z?WfB5~j+n0zE{>@Qh|t!5I#(7GOr27l5QDKNNAqLAZH- zfb_?n7h1i;{Fllz0!~Bc6`Gnw0*9bCb2Y;ix}ZE-*qmP{y3?n>8$(VQAeHirqu26Sa zd8rWUFlpZfP$410d;qsDzin3|KHy<>^Jwh#&b zGbmB@pz3NUF2{;r6arFhpS@+@aE^cxQ5Me^9I=2vn*eN#fT{N1HZ#!%DY#p^l2)+s zBJu##vwFckj#3J^1Ml9WkaWyCGCiXJR9t-*=g&W?|9evTXt;}IP+zP`-BBTA^Po@` ztspsZPtu^;6nhonDeCcx!oXkqA3_Xhl~V%BMS3UUjqG~w;eP1%0$~gP_SVA&DfSM) zTu4Q=9s>=wYQNmPFv?MJqbupu&y!jrD^X!(l9D4RU<&d?Wx|XLgyA94TS<{`ZI_YW|02)MrM#-aBjHm@buxNdC-(c{vWdL zfjzTeVHb34+v?c1)v;~cwr$(CZ95(7jcs()aZdI=^Ua)@`2|($DqOXyp1Lcrhs}&C z>3@%-wxBrX0yGdV;{rZ7G?%~+iiD#OOE|GpAVWy9{enL@%Xo+UnOq7p_d`57k+m5F z3F{#xa7g3%?;RhR{zd6=AY%s^MiRCH zt}j3^Ks`t=#(ETFnufh~AZrsAZaFfxXT4)7hK#9qJ|T*pK}Sj^!V*-x>P?hWWr#X$VwItVth z165!(QUeZPHsS+VA!xvwJ_(JG8_swf^g{nrcok-emk>LKiLv3T=sijatq?WJ&nUqa z>Oww(aNOzu8?}ib84L+jqd?jQD+(=0z;GxcE){I#sF;CRsVvAtO9=~d5?7#%_}_*9 zXIfb0-zyA0qAkz^C1)ji0m-5*86olnv7(T`3NB%gVg7@g{y!Zv5hr97){Ow%Yp#Hm zAjb&>PXpyD5stMwkdtL%J@}E~MqlPXJ@Y@)0(h|CFz)h5(3G>F0+=ATLNYjWbio(K zU4+mD;gvg#$*1GaPhI~zeXYZkW*W@tD>NdaBFX7O{ZWQ;qXZ8p1WSqvfJ1!7f#fkg z>~Vzt?<7mph#mqL$WBrL4Ea=2Fos%UC**`dswkAoxL$%2Q3V1Z`af#>v%_G4B@BS} zBU|rBy{dzLUIxoH2MOaSk=sBvq&i(@dHYA_&Jra09HhtpEb`?(%if&+KPW{2yN6^ZvD;Z;XLu ztM;DR$(M8D3lj5WvM5xI~jN4O)F}Z^!I;tk=Y&GO-FV8|LP*SKe|XQ?fL)KMe0BQoQVt6C^{L3)&@(5nv$Et ze8U;Ft7P+;{^%k)UBCXTi+t^9ET@yFi{T4<=ZeS~TyLb^9PNL)h#9*XNd2qX5T5nv zS7a9Xt6^4ypX{I#nz7l-$p$C781^4s1o8jWMMQpdkyRqMNsS9A!k6M~RmvWa^DMM3 zL;2J~WlUa=#H{3H$SwStaag3nM{H+N+_K(7qxvpEL3+NJv|Y1mveBjAUJ;nl9$h{? zV)Qj*R5%`@Nv5Tw0mRcV*Vc@X113LzxP>G)@nglu#sva+bmhUUrTaEQOtXihPAN5G ze}hh{M}5)|NQzQkj~pEylBUh$VFlA5)K0EKRq7VLA|QAeRk*H4P>UC^rDxEfKS)(S z<#koW?Gb|%?+?!CCWBi3>Uy=IECs)Mujm)_xO7A}&K1#ru8Abg@1lDp6!}oX3L>U5 zwMqLP`bgxCRP?^iUvBICw4Y}X29BNIC&--NzgS6{U$AcTkO zUofffxmwFZ%_+M&4PPpQco(~TXbON%FB@g`njCVxFS1fkV;E< zQZ*9^Q!H}ooS$7sYatb)MI=l2Ief1%4}Nxe+r96Z8Mgi|EGV zk0+Mh&ec;2Y79UCoJ`F`)mE^Y14yR{j7%g8OyGJrAjH~g#uRL1lbZ@(pK#}rm%Rf2 z0(Ey4Oz@vBPsYD^9L91q3%u+Rez*I6oZsgA_W3?vCCdwZUo2f8&h@<<>9>EM^b&6I zulIf3_`M&T?=v9U>-FsLd-}Nc^Yv_h-M!=^*6a6I`1^l<#@iF<^?CezY3cR(bof5m zes0=%*uOrF=l6cNyD8*<-F)xg>G%8C|FZP{aP$6$n5TLB{qeQ^Ba!%ZeD@Q)-@TmJ zemRa zuMbD>*Mr$-m+y=HYcC(Vk?!l!Z1sLWpQ^e45EMU||Cfb$d%YDGtf%hx^4s07p0NJ! zkN8dVfA-cTo42>STLLRKeII|b*AN*lhx7S*di)=r{k;FWy$9y^e%{>DJn=x>ue=A^ z^n2HgcX#z~+z?*4_Py8tlefR5(Ee`t{II>v_h?_y`#u=u8Po561tt72g?}r*cD){3 zn#=#VBcvbPHGi}6_Bb@|uWqgHUk0@<@~3GA^mXp~{U{>W@%6VC&`&$pyLrB!4Ei5` zFRy=mke1KQ-*fr?kFRU~Z?e7ZYF&D@pW5 zn|)pV-=2}0GyW|T-{z71wZHwoHE3QhzP6*o*X-Apzg0Hh+aO-vR-U(``*+sZ`+NK; zqXBv;(I0n9-)jVYem)PFr`m+C??GQ?-Y;(64=*=%@0&{^8*-Yv{u|p~Q@j8E)x~@r zhr4UHc`ZeAe1CPd?Nz_M+!FNF{{FssIy_w3e*Qsh+2*`M2N{XeSw z?sq@iecxW~zX^khbn^MAG$o|DC;j zKMs6iA=gNLk7vl!`}_d7sptEB_`Tl ze!oq>oofVp=>5?h{;VhTQNA@i>)-KXikpFqr~FFU|L>NO(c)e;VcB6rTGI zRIP2~>!XtQeMj(r`x;N*wcSW*tESK$nXDJN}uC@J!=;De%?^m7w~`n zOW5!CJbLQy_oetb$I$=vH5SkRb+shm|8(W=JLMAB`TYT-{qS_S%W;z5uD=D~1#G*G zxUP|m=@PQsjICV8I}2Ro8=vN1*Wr7A(wg;}>OIZbd?lI||L(`F^;><bYjM?*4hjmb_lyy}269aX+P4 z-||o)dHqy+%^4KGJoAVBrVZzhZuP&r8k|c#d0WlHsN~x##kqX846R`%YqMH2w>-8@ zzdtx;tEf$5E5GYsIXCsyZhH zS3T49J zlL40tpBGQLtqo6HX<=ejoomKswbs`QdZ2Rby?qaA$+KR@d~Do5$Eu>ekxZoBytgDC zUq2a;v?`Wf+v+Err?o7(ek+IIQ9jmeoaeI-1tE!{k#CAkmczSLVLD zSFISoZS}6<=VJv(~L)n(-SD+PeE?{OW|3kTeZzU{?LsUij~|^Q{N|l4)}A=SX4Y1zr;WEV<|{x70Nl-fd5vS$ zT90-t5OS}!In7k(sh!DNbGQO?s@MvJV%lNlKGk-0tu;=$#mm6l-zt^yAi7cpQXMs- zdevw+BG}$381YN(eeOMJe7PCLe|j9mzVNUH9oP!o4KF`Gb;F>#!{)gEEv-4HVc!rGvEr|M5gNml4?@04rB;&@&#qt2 zi;u7Oa?wgz&XW^1u+XS(1vo(5R&d$))@gi1vrovOqC@jAE1B5f6s%+mnpM+J1tT24 zE&7vEo4VNOSgxlMqTP06BDXZl6xEjDmR5g=!kQIbxq%(zDojl8WkzpRtuH2cEo|=* z@>h)jNWGklHImHc{+~5wI1#hqC?Ed7D^fti5Ffn$f`+c!W7TAOXgXy4Qii_2j;X#W z!b~FY;d!itK2r=uj-AwKe6H>$&v915P#!+b2R=aoq2FuSSV2lJQ%T9RfbJy z{%&)uJ}c@vdgMyWZFS#I@wobbR~|qMVJ*%pzuUsDb0V3V;AF~`v?&n4fZO^Q{xH`H zF_?@+bN$qy5rKNJMXvq(4?PXttuTPTRKJYV}fkwpyBXK9g8_Z46ACDgSlAzG& z_>HD)$ZYis<0OW-ZU=H>?u9z2DuU`mxY>-1UsKLm-Ut9yCCiDA6U+U}ZUDTXeAKg) zcT7p)Zj@$%lquhwAm)%_VH&oAaCW{s^w&~a zvFV&!#O@X-i99;}AZO&~i^`+_B{V@?ml$K8`UVGmWYF_OQZL(!gDLsHB0mcYD@^n#4!lVf*a(XP6gJ7UM$U1XRa*0elWocDp3NJfdg*RNaJU0Q4 zP98J8UGw{b2q(alhg_c=SOtm$by)9xol!dGFWM%O%VqRtsHv-x;wZUKror!$ntujb z+a9XQoihDw3F2@x<){E7J@JT>G~oBCqp6YgcARfp<2HE2>I@PkrbOfkEZJ@yk*kpo zGv2LH`v4{Z4Dnb=qZ-IG?CTM7H*6htC~C+|_oN1%L{r=slkqOi)Jo7jM4<77z~b_; z2t!P2lSAr0NOC=@S&hcz(a9LF3cb0i0I3j(-7U`{qSk}9*jD~Qo+qmf;6=J=g-i zOrK0`-?T_MTR08Lw|Oz4-1fqBR37$|?JSGbSug-QM#-2w6uQ`c(;C9nFehc87LvvV zpK16im8606feQsf?e!qOdyga-qjfq5!TKzUGAmpO)LJV$UzbB6#>eVQ^F6s6sEgk*=-ThOvw{_7<4G*7e1Do?Hw1Rxl(817fLohMtn>vY3gK;P}j-rwvhz~ zKp__aL#<_dJ)n9>{a66yST3ZZma^fu=XxdOq7qPjwY-WRgko7?aD;6~j$y0>KO}85 zfS%pT9rOj%xh}U5q)vXsBo5xezp13;Lnx-^Ze_-GIa@RlA`Uyv4>J_HPaIfTPUD-k z;tYK#2A_u)f$(~b4Tdl+*I~C3gqv^!q3|Y-3-q@-_n?%!wToH@3h)yu`57mV445FVVJdo|=a(mWA;o{_-46UU`)N-YW~&R=2Z4s52* z0us)!6dBX%T5c~ij}Qw#U5KQq=FT`V^hAF1%@lcZF0>e;3pUR9gPl5l}C=did2nH|3Jo!Juf=q zfrk^J@=%fnXT`@(C2{+Ult7qU1R?ZH=BNtZT>I4srtg~hn1=}ibGj%4w#m)#3TIIq zUTd_~BLzgC)*?|vYEvi!asqN*c`Sm$O-INr*vm(0N8wi8AwY=ec_r+gAdQSJ>(UvN zb>rs-#l>;WAb4j5vQYLsO9K|noRh%)y@`KQcfztm80Nh)=mF^Km>oCqHUyc^Z1|!j zWUb7F;}%oHP4XQZ?nXw6V3S>q;0B)v2<_-O#IOjGPYXYa+o`dv&az-GB-7Qv6E&Zb z8>rHrF*S<&1xcAqn}C>VEMws)Vr^gwujbaYaLC_7@PMjKx6H*ZYYBF{A+s4YmD1E3 zGcZa)kAZz=<$SSU6vEE7<*DR#>Vpzt35NH`C%TBKyT20PFCC?%YYZc}BdsO-I_PQ4 ztBkC+4oPE!)F5dW$q_C?(uXIGrxY)NPSNMC=MIz*`3wr7!xK*|&uS`5bAr*x41Y2m*!WZaabN14!1(B)zGMJhKR zJ!dX}o)1&cbdn)L71ko9+Oz5^CxSvuSSJCvRA;IFx|7czSlm0x#&AYZnA1N2?8k;Z4#h1!9CmQ}u{oP=Fl{j*S7NMo`>`V8BtkOk@)ufX?OM8c`0f zSH%B&)(<*}GI01FkCOrxL&n9?desY5WI#`3t+;4BEeNGsIncJ@FMP!&)qt}LtaXO& z=Q9dY&WK1KGys%U;HaBQXgVEwRhBYn`xfkS30QfStn(~DgIp3k#}!mS-9sRfhgsds z;sIzd(A-rQ`Jy{;7hp6eI;>E3rvpw;tNeN@1+j;$fM6oZ#lN%rbH5%*ImfGdtZQl$bVt0bMra3%=cs01d-?=HCrW|LQpTgFWKX#$ zhBVq?2%oV0=`av60*QJspPb$Dv8A7494M&Q`oXG4~bS}tqqV5mYgLT6p3gl>O zTnf<05f{6X8^OH;Jkbd<2IZpNnZk6e--`5wmsXWhMH4BYY%oZ;8Tj#m@4{ES$GSqy zxN#2riBflCu-7H;04)vFes2J*2nK35&9~IbbXE_ z7z)A^Y#0sS&CHj_K>7>j$Xud1%mmEI^O6OGfyR}|LQ@}DuqZa|=7xABe8j^FB#kuO zwEj~I26mWoR+Op13ieMZDe!uul7_p-Z^FnWNg&%6B?dxxDKQhUZgggrT{dFl(?R@$ zPwqHu(`Fe_#w(FDYlz*07niYE(;LZ-Vxypn`(bzttTRO<)5zSyN&<`RV%@~89F*ao zL`0v#6tNCZ+r1IFXRf+~^1z@-Z<(Sd3(mumKnk>}MuN35^I&ANU9GQHSBwGLK*O`_4MDjt#9w)8ly!SL}; zDhFv0Bg!@40&GBVhbM9eV7gK){$u7)fOBmUK~Kff#hEh@c}juEOC(2mGF~S?C5U}u zvobF?49tyuu*#H&Ky8VppSos|CL?KO7EMLA#kJxpCVjz4MA~XK$}WilblpH&DkM^8 z!AeB!D%jijJ-+;#IOqfW#^^R&W)WazkYv~05)zxB#knZFGL3Qwi2%uK+NhnLpR?MG z)EIpY-gAJILRU!@tGb7Z$@rdtE;%tMhbdbWCg(uGmChu!#Nru**l2M^Dw-`fXRrws z6>tbM5+YM1yGcjx&}?Z|5<5x*uh)#1r4px(RX@X-@Pe#GC3ya4^anOqZgU^C{0Ls=5WshFQ88=@G*|Ht2inZVp5QU7(+vNQboxX5Pi zRPv%6MOb1jkCkM;rv^%1X5+64JSiygC!I@=r4_CHw*)z<;ZiLWG9>R*L*%wBJ1fE> z#8QEr;W0JSs`@^x=?R#F7}jIc839|-o&g&TD=<9;JnHi=38Wv<=$2<7znvayJy#y} zcrl#VVuAN&8;ROi=MS#O(|`qT-2!DU1l&VdToGO}8(5YL4`H`3ydF8k@(nagBHtdV z;9S5kjKUBRkQKV}-=$@qo2#*K}P3THexf<@v3yU(d0j8`IeOF zSwvUrwBz<0o>DdJHjpeVO1~%a2CT!bOb5_w39e;4ZC2n*WI)U1BuhnfQyztAUVy|X zPu!t(G|wun4_SEUSG~zJ*hkr$RDmLPb<1vWt}I^Lc^&9(h=YG+OSU65SsR4IgD}G} zwRlx3y8(da#p`k|@H&t-#4k6XnKe&lo>3#R>G&MQ^ce{Wg8mpAH`G$`2XR5`x;-gh z4IR!?2O)G)h)YQKAdASErZlu5OWF2V@s-P=mLYhC+Wt1SUx}>_s-yGzaeB zn!FE%XH7I|u`iK|g@(Jj2ly1DZ`!;d<8jl>TjaW)jfmc2JlCHas3dib5LB=Y{9?X7 zr5~4fNHA)HjxnsbEg-46u2O@I!Ci+}|aEA=XnNZdT$j7mBQVQR~B`;{cJ9=&R;76!8S zH7;@HqRsjRCi}3sjk0TE5f-hGuzU?~YFHp2(idyDJ3H%9EO5RXLxe{`&(6q8xl#F^0xb>MSK{zBMGp%&p+9}a6>`ror-$rF7= z(O;KVc+St2(TQZ8MTGOLtcb@O1*=%tP0{y`kfJ&2y4jOixdfAu>oyS)K~VlChN@&k zU~`}*GJ4(mlK!-sF0wTh-^pI9D*MaxA3&0gW@O|FXsg~}EjDw7== zbd$#Qu}J6~G9&ZHy<^B}238RfSBOWuz~4dE)mf+457u4Ju$Qb1l^V_NLMG}Qy?4* z+ZwUqFoe@llidiOlbteq0@BQmg*R#|H_Q5Q%vT*O63^r&2y;EUC7(h@8D_yR{>FWo z%4lcSNCARdwC#gWw1`vGiV4K~4IVC;G*Di`{#a;imivl;=s{XH1RGPHjH>Nn+h@a2 z!$NxRPmJNtX7p`Z-z_wS&6rHWQi4<(u}3V*>i@B1!I>wJt_sS8S;}HYdq*yTj_JUI z1tlX^JPsFwmvl&OWT6J8`I}j#+!A?%3(Qi-2P`t*j6evKSnmvDU60#Fr38m8l@>aw!=U$x;iK$_4lD0_c;TTP2 zdUji59{eEJZnS5~r;;f(FQ_nv6)|a{DeVd@$=cKs8GI?m$@TPm1u9}~SK2ma*s7RV zei`#=KxI)vB=uD5Iv}-6vk|0}<4(1F6Td-TH+9oWEldWQ``2(Z+rFWx+?rQAL}R-& za9w0YW`aUMP?SjH>BT_eB0?tyPaVc_82JMJEA(x+;)dXC`whhAZ1M)E6%FLUIQ4F#3HbK9ga4rHsY*fIz z;c!m^A_-w8A_ry9}~Vudfg z@W-!|t>IO$MRvcMIh2;fd0ICbL-3+3yXA868dWdS9+|GH>tvaVgh%0I{6s(-n1+O! zooXIN>cX(ufA=wdQ>CH$c<&f(1iUDO-j-SySdAWS z&eK?2?eU42wy{DH-V_Y2r%rpH3G)GuveBy?Un}8w@I#9lMNF?}cg%;mv z@{5@}3CQQ1o0tSB@g?~WWQqpmd>KTsOBX`uZwDA3bsx?VNx=hntXAFonc-o_B>pHl zzS*I4Jd{Z=y5jzE|CIGcs5xT*8+4#NOH%TUshNmVp!&T4UJ z2!Vd<2*%s!`g^f_1@2#>I4UFcAt8%oGYbNdYg=CJXf6BB)~p<76Qw%HV{ja&&`q=b zuq>^w1hP60a^93Em2HTrR-}m&T^&GwX%|JO6U|EL)Gx|O5A;JT{FUK09+Gfb*(UhJz_&v7uKQf z7RI_YcRJDwM22TDD(iyYV8#i2A&R2&O5w10@miTYU$#*{j+$!G7!hUKDJpMklP?0w zyfRBGl>k3q9JX?jUK>tcu^c`ey=In*S&2GM&?MZ;^2J4vHAF>#S3JV4j!vu+Bzq;^ zoQcs-5~v)mj!9H@6_NndM(;mmnS^r16d;h8otfl`cj55 zzeJec!|TDp2Mu2EgvGki80^!gBjpy4p7d|ULFwnQOg3SWoR;P+e(SK6n;{ev0AEK_ zO7=jEh-Vvtm`<}HbfNQAw=ZPsBAq9Oy|LET|NRR$*>M1kz(8>Cqv&R-hx^PJQ9Qvs{=tUXox+DFP{og_uTE zDvBk{uVhB62b3@BFdu?hgkJ$ENBzt?5K4@uhgroAM01vONkpg3G^Y5-Y8@4u-l`XB zlOsJ7i4B%;rvzc6M9$p^ZX{$6Wea6 z%|od6llXZr<($|gG2odq%Y^GJI&7;mAvKFElqg)uV9E&YSt)+CMge-p?GIH}BJOyd z(#(>26?}sZ_%91puBO>2yr@pTLo$02{_ysVwL~6R-9_SecD}nJdS+e*5ZE?=r7R`M zxC9hiqsP5>>!7SG3e-ARxVc*@sSS{*f&gDjyI?m3n1oVJ8Xy9;x3aF)l`h+8vLXXQ zVMrO0d?DV|MRS;()$>_C6llewc>#Xx(&h;wc{iInGzI&~1a7x-m<^jPG%{xXaI7Gq z6~rq_9U&*LL2nH&A?zwgJ`$(EmYSjnF9jzNx}O*Q2pP4he*3|bGRFcb(*X&Lk_Twu zXfGe1q#E1(Y|FNM{DN#9$awQ{B$5_Ht;z^*LF#bet*j-{?5rW}(SyCL+mreMMbOp* z7Lkr&aP#3bFw>G5>f+4PcL+d(Pv^5a5rJ2Nk@4*6|`f(mi;bb%b6 z3cVQpu&3s9gC5bVY|Hhz@n%{c0zdxNBT)Eo$Wpsns`fA7eSiE+xl*8PQQ_oLR-`KL zS@74nXM9;!o}$JPk5&1qEk;#t%Zu~9l&SPl^=wz(U8ju;puhEgker7|^H~k5H-Q!! zPs-db?U+{YVF!*ifc2M~b22~=fjU!D%_a3wnV=#;Oyk7X?9UGJ+1q?43sU^>E^cYLL z0T^&5F^?fH+*hWuR6D zFqHGKnpVy%=kUxqK$Z{Cq&7Ur`)tCuXgA6LQ4#dG2Gd5wZ>TieplrjGmt!&8PCb;A zGWw`15V>%pn&HK1K`5F+UySQ9snjs%iYpV;3d%-Zr3P<&=)1AGM?rMk(AFAyKy;av zOKpK6hHfP$>yLY0I#qXO7YLrE`}jk^xMeu{3twHVyxe|h{# zYzg$f-|Wxox7pw53w*`$oDg)P*?N0C+HmG6`e055v87*m%HIQ-l2!H;o@*0JlmC~wgnHqvc;yJP! z+(%Kkom^+|ia>U^w%6?h6bk#2Z&D+e`s(Oa?qbipZ%_MdNqsTzOgj01QNq97;51m2 z`5izhFw>X%PbTU^Fe|V7A1R}KNDvF!gia;8V<$69a-qOL5)pObQwU{B0iu}>ds~e#f&zW z@_rzgDm}EMd}pwry}*~dRAFKmx;~YlGE^Fto7YetWO5_`4{D+;KoGuVm?gz<0T61} z4er3rxr@<&2wl&{6izG>@NmQpIgSd1S6GZ{`OqQ~gN|%j-kuCbS0d@|5cCW6BeO!0 z;x|sv9!3KM_QeWU6Nm$p!jcI*1u|FW$9!Ngh(VG!xo#lz036XXsnv)Je=9YxWug=i zXEkSKCPk*v03QSp*HZFP0~By`Lb_S-VpN`P5-|CUm%_7L!)}QSHhX^pJcR?PDtP5( za8jqNuq{cwG?-zb7hFsd+9N0eje!>E4X;f`f(PoTjNmHiTUjN_BnSgGOc0HNUW zD+g+qC%zir=9!ZZptc7+hof`@&7FG2kFKM9PGUD;iFmRhvk_ox+&W_P=K>&Hq<2fi z3>5bR&PfCW@3IL0BoLlGm?V0cpe#G+lUZGt@sx#Fh~%i{*$e{nuq3(Pd$p2|=$$hJ z@{+p1a+3SxjuyfUWD#sj_3g2ny3YnQAKW_G9clN(vs9O#A$XkNh@fB;W=V^#S(}u8 z6qbfr$QoNEcQP(H%CSr)W;&N}IKhVPg22VFpRtoFp8KJO+@K(r~Gry2+mbfBW*26idz@hESD5#CAt%l(l`QAY6bj^&o(FhKxsg*-f5;Te=@ zG@K56JNt>8C)ZeKt>oYubsh6Lw&MtrW_kBLG1l}@(ML&6|HhZCLI?1YrKiS9W|Pwe z7y8h?Lq_0{^cF316jo7l_=JihRY)}!H%Vj3SNXE3ma0L@u>)!4f^T)K{qGG?2-w?s zm>!|)QiP_;oIv=J?W~66O*1%_c2F|GA{p)2XEeWNXb5JqeI(`PP}n(u4G`T@ zhOvbrGhoy#%jQze=}|GMoJYBzYpoBAB)2adrofi=v%KC2)z=))=o}T?i&pW|-90n2 zrpC%1kIqdYNL9{xxw%zce$9^3+(4VWj89;?3H{qeicJV}UxgNbd7mB#=R|gj0#{icRp(psTJ;v=#tV8hX_d{S@+oE z8nIunoSTQ;y`fNtC=d3(nng5$8Oqf6-&sYZ+CV|$A;?)=L8m)q3m;5Ri@oq;CdKJ3 zZo+ESFVW+nTe8aAHUNv9mNFw{1kXrtAJJ!0hMBCX4LBZqMHTVcoUine93G^_814HIHWjzU3 zx55Ugg z@_0W`4-)g^f#l~fqk?DpiT*GoA!)|WU^S@|>H*1NF<0;bU+TnAZ>TIRwK@c!VwF6r zC5oMt^ejn~C?zLE;-({$glpcaK@j>g(?SPTi!AYkOmtFoD&M8lc{?fDEL+ZifL7Hg z2ndfMuL~}U*S~$8puLvcyZ>{mwhz$Hs33a zq%TuEYieQiJ~?eUy97T@qtU(F#Z8Eur@#mIWbP1895MDM)^%Q0@{ohH{*X~NqQKyW zcq*va*-){;>ptVtumCZlLN+;rf8Gr;kt{HU*f-*fWyvaqQM6CCgc6mX z7^qCrU!ZJa--$s}_K!eGujmnDXZG2kPL4lTL@$aJTSPmw1e`@n39EF`cS`U^FPmcH z+1*#l@EL?STRVa=SLPJzh^CrzKXI;W#OGvMQ128@kW&V)JC<;lLhzoyY2r(aR)_e=_ zd0Iri`BEPO+eS<(dV}CjX|$w{TCLkFQU)2^woYh-L1YM1h{2=r@Z%pm)QQ}1|4Thb zI0&IGxDc_3 z4dl@UrDb|)4Lpy_5|HGlBlD#JnFw+xG9q`U(Cx=KR*y@acJ!Yk3Uw-iK%eu*qm>&( zii@}=SQ=$Rsinch0GxUPg8UL0@LiJdj0QlvXdrg@OH}Qti;PmQJZY5Wwd_0XS*1)C zH0XsX`%gmSsp(M#7Wl7%*|4Bm5)3Skearz7glE{7ILUz&D$3KY&Jn5*ej9g6gxhXY z9sPaH(`dNt@iMZ{C+RUMxMtSk`tjB_;`FjhQG_o&3*iTk*+`R>I!DMDM48kI!AkewGWiFERYx}U*uIWD}Km5TIx?)i7vb6^XPAf?b1bL}qnl2So zV6mTbz}-kNYcHII!*GoFgPXp(+Y))GuHZ{T7|}%sd%O!8Pp_`UX|iKuqREJu6OsU9 zK?n$yQ80B#rj3n&Dc3#@kR>_oM z8c_*Z>(L_=TUCFFG_yMw>cVs34q(%TXo-HB!@dyH%*s;Qn?web{2fEagafYDLgcEf zD412QNN*l4aOBPdMR6@!ClX2Tp7!82sDFMkP&Q!A82_Fv)Dr^DJV~`QDJw7WHce7$ zl5fnVq8j3{gKA`0`cG1pY-834=cv6?2C+<{ix^kmXeKMecl-Pho=JTmTVR{%(7g;S z>REn)e8%HgP%!3_ti5$-#hIa}5l&ordK=b@WWNa!XI7{7s<%*T!ixEMOW!WbDh$O4>7 z$9`#S`Hzs8>okVNIMr1KZy|^?x{ZocPC(g0QX#{I1LCh0qA-x~&%8wa0VIVs|hJaovVjldZrUZ#FyL#$|tjWkg* z7C5n>IyfP1RcK^EgM6s0THv6mScnJ~3}jL<{V^$RDR>ubpd~vh%uV?6=s1_6?e8!E zL}YK6y<#-xzFL`k7(fsP#b+sIGdGU%8U7kiADGVXsn#f&Bij$%ASw}n0RP6jL?bF^ z7-q`e^^lMwQ9p48VD&|b0Y9+pr zkC&wITcncDNRph)*osEp-jw*9{Us{S9h|(JGJn71Jjm>w(^{jOMlZ75_%cOB`%i^9 zP@KaIzVRV4&&nx|+qf496dX$I0FP>W%_J>re}<>omqVKO>Szxs9cz8pWB9)FL3!=Y z%GDVPd6_-u@Ah``dQfrXK5EFtQPeQ5bx%tb&FJG*vm(;%-wtB7$9H`c!Nn%n4sT=_ zng#|b<*DUR7e}&F;P>GdAv=v~VgEn&gkMGAU)!R5I})zzdqm86V?KCnBW8Vb+ax`# z{?afAvbhpp2m$uBg)PgHGaM-j!FlA!Q;Ae85T7{AoaMGCsjBn7JI+_OV-)8K&F58* zY1Pf4_sQii@EQRO(_r(QS>1{1JSlEzDosX-sNlXok&y|7Bp8OC$^dn8NnaSwSfq-& zrRqd?76j*`7oDo~l1BD&^O(6O?;sUQZ%>f3P$WR_Ucv#w&l?4+uB4=}95x14#-tG~ z5PzxLMWc~MRwp;gfV&Vqy10%f`G!vEY8(y>YBeUAQ3fQ|1h?UOR_&`yMYmh}WM4;+gGSYT0sUBtaetNQMEJT(;70@kA*vK72 z5Nd_zsvjYm^6G_0J1pUjyu;vAj`0QKq{V8DVjs2-lsGryrgiEv@8~B&7!UjH+D||Vf;Rf8!WFr zsjp_F4vVmqn#_N5f|dn_2ID-n`ID#g{#zVP`p-~WS4h;tZpUeYV4sk2$Wnb;4MsK| zSRpe(+qvSnpeFqUFIoVGhyO~CGQ-%bQ0R5jwgO`q-OQo9zKQSI|8M57X$Kz7CX@xD zKPI-haQPNmkHU~ij9FqFe%6lqelffPH?S+Y%HI@CjeeAgsIR*>wHn_>n3R~E8i`}x z$|)rm_YF*Ff4-#Vrvk`$WSRTx^bpc?htPYSksIhF*rOR}1g!2)G%=KnwV_0tehp6m zXGhP-ocsU^VsiEr`V>r$mqN=dy8K8`H`T^Hs{C!=)Jfnk`A9uAa44PZraIwaxfvuh zwU(0R=o{LpO3Dvz`^^FnSoi29>c|Rn1j%z)$|eh8Vf0E%xjDf=D2t;}v}#GmhOnbn z(%$$oC(Q?~ev0XCQ7ZdkQ%P9jEprd~$kL{;*t4)9^U{0PBL!xFv7w`-xI7mjisY-) z8*F7xN<>bUiERUD8+h^7=DFuWErp=R1=Zocc_1*cMfo#3&e^epz9k>fJL@$$;}=w7 zU4N0}nI)phxu68C4CGj`IOmg^xN)_B49Udxj2KvjU2S-88PJrt`304n=5(p7Fd5r* zVR#QO9F!cV<6OgX3*P?o69)mKj+*DNVu&>GqC+~xoKqlHuscl60;QPoz#fN0>Bc9Dr9>5+zkHUg{vqo2 z#O9Xm-^$nhg$URCq?RVSDz0gO{{N!uEr9B3wryeDCAcNH+Xe!G;O_1O*Wm8%PVnIF z?(S|0gy8P(vayZ4oqYG4cg}tPU$dy{>K<$KY?-~9wR?>PX9WuVrUrd&`~}xN3>s%i zeFgG(d>GB!sznE46c8B|hkF;Sw&8(+YN#6GA8n>zE5*a>jG&x1HnU=p?tQxvzVFLXt2r!qa`d(6JRLx5SMXMO&h)S34PhGLEO3RF zV{2?!;`YupN=_RrR2|!?zb_^6Fh~89^Fx9cgcwQ8P=}g5vy|Se@2axTdj}|XbmK$j z=968c(3lmElCg6lZFC0LiPF)=isr`Y6`p*^{?hqE`jKg*lR9Q{Y;Qi1!8Rxme|^{i zqb;>mq+W7|WfjGPj7O>8gqEp-A2jGx)nf%WV=SiNnPd4;VFpCSZ8YN%Fqq&jtql7^ zm$*L|K8u$0Fmh+MQZib>WkA?_{Mq912Y)%Su2K5=UZXq9K5Pj_`I)mUrF-7+WJi$G zLzH=C-DsF@7sV^*3s4Vp@O4>R&wd6t^Q-BxaxgTVim~=LZTP%;*a^x)nyxv5bAfRB zI}R)#we5+rQvYLj?g|Tzb8ye$e$;*2&yH+-0DU&$RU?9q0 zI0W2hz2at0tUwFZe&ogEq+(iw^$B+z*-y0*bGNqJqx>>?zQ^jQ(u)r}Q5&2%fl1G` zX_bKg-X?0xhIW%5*GAfd^h=uJI~AfGSy5Ug6SZRMI_#)6=UU%XD)oSs7v*TdeRnry zV|`*F+|Q8oEtnSat-&^>KZ*5TgI&&P==`UtCC$K+Q7vVL z6B7wE^|)R$Wat@!pCOTJ-(AkDnMemto=7lz3m%UVO~M1O+J`5@CC4_%bM3Qv!xe@UWz z{5tKO5f+1FU`Bt4heKG22q?h_?<0*N?kqYs4SB-Xd#J}f>dROIxFoA0d*z|H7k=j3 z-^GBCVwjGcF2c}kGn)3f`RAWZ?sMSv(MU{AUv9{b|TkW}Ga>Y%BPo}I%>o-Df6f`wPi z&)ZuGF2cJ32(gz7R!MYecO<7p92Rt1DpD>IHkp$j3Qr(}=kGxp*tm~Sc>|YXU4}Ex z`aR~5oUVYg-X-XL1RW^C-(-1Fa%^|1IDL8BOLFzEo)a=7TPCR>aUlX~gwJjkJjwXP zLV{52@_qx!S|X0ZXPHH)1FL*jal^2FADL}(&cD96uI*e0_(tjax-RV++}=a!8Ie?c z#wL8H-7ryfEqKA5=)n_B=-PW`=ec!)T@e93*|;>np{pz|5=^=sGD2MZ*uSH87d|M) zj<>BWQuEpWeMI&R%Zt3+1f&X7n$dFzRm!DA!8X%AGJ*W=j7KT6iksCcnbj|%EX9qP z&|Z6qtOALwRS)t~7o;Oz#IYO22!}&6hUHO_Y2rr8p%ZMZWx9GY7CwuCz*0_NZxKsq z_T#CZZW59#y5D0<3#^B8RKVr1%fKVIyYzanT;0*d%dABu1T5iJt8dlz)@2xDa(5+~ z>l*GvF~TrGrf@6W_vCLk;#Cv+7os`i#!SUZM3G<;P9d{AIORTBsj<%#bNRq3la#bNN@w&YWyZfF=sh|)H8G%VZsdlz)pMY`bAsQn1 zL>E7|uq2#2n$A>&!k>2B4Y8iJMr7|uCpK#X%z2fULl(QKP@w7@4Q8lMuPzoaZcD;< z*^BUYACqQ!&-~VVIqMU^P#RvWA!#5jfZDCivlKc&c2?r6r(mXqrbnT;Zr-dgMDuVG>NH+-plW zkkJ*7&Zw>vb(0mWn*^Y3QK>PK&=rNgF$hlhTuSnJ5`&i(&K1H*N-($B%ta zmn(uM42_Cro|)dl6thGE0a22%EZaPz=F?V6%L@ETOO5_B=;cviC1d2x-2t@m-|847 zO)GbVV5ymSu+$9R|D%raA6heVb}L-(xm!s4g_IFz^?PEQPpt{v>M9c1Gd;X7eww$hoq#_}ADj4Dj%Lg}JI0(#m(p(<;MkGH zTy-al%;2VsEEYkRT39gt948$yy7*x)TrA7!SX2fO|JXZK`6GZSE#VmlWp`?+L+3qy zUU_-bkB}5`9`EI#x)HG2%7i@7%2%^scOGd4 zCPjKeb~5P|cJRE6m17UAHY41Q^;d1?DxPz`uXYBj<=i&SZyZu zkJ?QAI(=G!JH6BVHz_Xe;MA2KZVoij`tJxasGG6p>sT_GVI4V3+@gofgqe0$7$6fy zU@-dg^iL7mPROHBu-eQNSZzjw*FE`5+^obZ-#=HQDF!$Q2HeaM^JtE{d4Fai$@6sHxBfpQICOO2fkvk4%8rhp{vjkF*o! zosx9LrkT~$W1fU1jXEV0VxJ+>>$#j7;6YVnlY)InH;r@e|ejd)Yh z;s=bZDwLOr@6+Gr#7u9`Y4NFQzO5%RN)c_8;4!fBrS`bJz>Zsmf$%}R$1@kul!!07 zdGG||BlR%1ngqrmcJ|4zS*_^hm5#p6yPeIMGk^0evg?X4@td%>R`K>Mc?&r)rKX?v z{oHr(R?WX+;X*ge)foU`F$XXu1fadE#;QKwc+Ugg&??MFJxl%+s*yy-%pdYJ2bjG& zZ~$2s?Mtps5PPfF!-!OL%q*960kXhBdCSp~b!bcxca{#~!SZH=;RUw?Bg!cc0Ac7HYM}>k%OQ`8+y8@&etK~6C6*S zi_@jZA>F({;2g54?6Wtek^?(xjblegS6>&eYI(p$)=Pf2Cg^lCtzDZCSS^byRjuNp zJ}6G5E*JGiX;D50;`LxJ8p<#)ld+=5$ru+^Qz}Q0k$}(cr&K71nb6CSNW^7ODL1Mv z=PRu1er(gVy^NKQLQ$xRs+W#jEiYwnXRAxMN$q7e)g@Hawh{EuDqGjm=M&)fg~aqq ziJj$=n?tQ?Iv&3X$v zf0jae7d@L^E(F;ryUBH>Pb7hxuFgs4)ukdjE=D-(8N_tyRVg>dUs4dg;n!P?brmhnc>HH13IaSG0>!xfBn<<#t$QT*NqypR z#Xc*RgjAUyIY)PmOex5J3!@QupF<0L#Z}0DQaaG6>0wRg<>5+A5i1sFR;e5}f;%$g zL(2Qe+sQGVpVvd@5MmYaEawoV#JkmzYCLe-ZQcBTYMlsk$al| zA>B}X$x)_lhA!z66Oy=S-qEyrAUA@%B;&A+gVjI8{$o#F%e0#`i8j=PXRmNz?5txe zR-pS~<#I&Cq|lEV_jlX%$N&S2l^beut%H*IYGfiq`v2?7ZotB;ie=eIb*RN`vy4~HMr_OPhgQ1tw!WUjc_ zS|j?tyu`$Qk1MdITG}S!reJ|ZJ10lRW`j_A!z17_X~UywetDj58f}_W=}5%4cY{(U zTJ4(;1+j>)wG$M!aShCzkmVl%bfhIG`5Of4%LX7?2uw$crd zN(xOHz-cnBESvt^8Xvf26q{yE4oOq_-P5f6jxiyC!;~ZGt#pQP8N$@_?YYJ`XNJBM zn7}K2k~*38kz&rrh%s_lAbV=V!9Q#uvEp&*hz%8Q2wE_QZMC% z^&lp@b`@(gAKbdcx#n?GQQ!{?4i2Ygm>t?BCPuaSLZ`@?&MhVyyuCuCP>gO-o8kia zTATr2<5mK?Xs%OA+NQYG@R(8&bk$vA1SSU~P?9BO2ghWt&rRW=KCPa6kFX|2?G=r; zdd~a=Imh+#$GJ{8R-_uAmEdE}EJ*sCT)r<747F_oIbv`l+8!K)F-c|l6fTlqM^06F z5RG!LxeM6(RpbgOJ9_3cRT%0R}VEynia!hC!@@7h=b~8h}+p;3Tw{VmkgcI zBqcD2opy211aH``F}@zgSHx|nxQ66s{lMfeSiGK(#0=Ig^4u}Mn*Pyl;KE2sFsi>8 zMYK@_ttf8vCEz8T`+$@5me10qEYa6ZC}xAJz*U)MC_%oVAE~%Dj@C3cq!U8>pgOUX zwk=>KDuE}71C|-4rgolJ*$g@di;@j0sL@H#Qe@S1oi5S*XhM=w;pb!`Wj;h>C~jmbdP!;W@Io+81{Qo4$YQK!jldsdnP|(T zXC5r+L|4aYDtYkm2{LWmlUTNrEp_Nwf*!fLgZ>m;;oGAfKCKHUc+BBRYDyS;#(6=y z?^Kes)z(5-TyzlRMmYM2teG7Bp^vYjZi8*je};^f>njCl|1QAI8nA^>l7yU$MW{%| zen+aNy&gjkZ?G7!VEv?68}F&hiHhMNL25zM%Yf#EUE;-)pcq|CBUTsHaHI8if zz{!$xG#w(t=^7*Q!IYd>Xf(&N_?G=RO#*mZNrr5rTpX^A4ax51wQLdg{*&`7-BhWB zs|Q3LE|V5+Cb~f=`;IteII5yJR%yJ)wf;%463piuGRkv!XwncYyNx8VF3jWV#S7yh zj}jEuWo#*T2n*ff)6huD zwC0?!q3Ah=Br-MFHky`w5m*aDKvf#x`nL10cPjUukH?Tc|;w7$^rY zALY}eJ6u?uEL|hdgGtrgoXPmr?3y2v2pIZPv8AJ=XpykS@eO-F)sD?a!OkI#q)f50 zm9FliY*8BptZM5cV1n!{iDL$atFE&AP#3MD;I?l^7Y>RcCq7F@gM0O0`X9hbF7sjI+{QKs|2sTa(P*NgGlN{B=|)WcTkn zT~!as&1eJ{qoc29G#X(V*kbh;as7J(zvUD+g11iKzBghRpm1DGv!h5Pcx&dF?5U|l znfu!W;6CdxmNaB4cLhVHD`9!;O^O=PBlf&2w4giAAgb4~=S8MA{*)fSONQqlfeu)=7Gw6X=feI^>dpK`u;|C&U&U_YY(Bqk=mg?e9FbT?4)aobLREtCaH9M1Gmz`iR$p{ z1a#nXU0Mam%e<()=R(QSdFrU7!FN`7j&a-BGv_l$yZ5EEE?I?L4MvflDI$5FYS*jK zKpO9&(S%Y5sRSwR+H{LG_urb=AHFa_0=~g zC0ENhmFQ==28}i(*z&F8zSdX-=>l1W_l^jq7Dq)}+*04;QaY#&%>3;L{5zt>bVr(N zy{{Qu3codPz+}2?G(5~V!p0P5o^*E2qU0Tva-hjNO%eNExYp;v5r2MAeZ$xES%_Tm zomqM&Lc>S^md7W1heDxtndQ%y8}sTs|DT{yX!-&c5sX}fU> zE9e}}*_B>ZxU6q);Fjl|pRrWJW`!NUB$xH7bKbPd#+qj5%ymghH`h`v=2qq-yebGe z1z`k}N)$nIm)C15S!8OwK4qZch7&y*b*M5Et)2>{6I}en6Wl6(CT$t6o@saf`1W1B zQ2X6#z1^=>+YVs6j3@5#qMtBL#N_Cq^#piV}xK&duE0BC*9l z6pMW_6cH%bLt67eI5y<-Y)Ops<5Iqz^cuxJPJ72X>Q096aw?9GZD};g6WGnk1m-Ro z8W8dAUx?V_=8KY1bj1W^bDMr)

0ES;=wk6t+&=!D>dC!j;1^{k(W@jEaT`_0?AP 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 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/colorama-0.3.7-py2.py3-none-any.whl b/venv/share/python-wheels/colorama-0.3.7-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..65328bf9933aeb4f6c7cac43a2f0372717bcb8ef GIT binary patch literal 19917 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 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/distlib-0.2.2-py2.py3-none-any.whl b/venv/share/python-wheels/distlib-0.2.2-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..827bd27bd5270d6968d13d0ffe60944844828679 GIT binary patch literal 139349 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

EE1#)M00|8j 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 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/ipaddress-0.0.0-py2.py3-none-any.whl b/venv/share/python-wheels/ipaddress-0.0.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..9ffe578c9053223a9f9eecca8eae88c7e5375b35 GIT binary patch literal 17454 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 z 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 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/packaging-16.6-py2.py3-none-any.whl b/venv/share/python-wheels/packaging-16.6-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..06f5b148641955abeaca4b36c4386a9505531ec5 GIT binary patch literal 22701 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 TCKwoi>l1kGEz;Ef`|1Ax3`OqG literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/pip-8.1.1-py2.py3-none-any.whl b/venv/share/python-wheels/pip-8.1.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..2c4bff41d4101f3b9bc77239a097dbb6837fbed4 GIT binary patch literal 151984 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%0A?c+tVj(Hro%3);O1-=!8Su8nJ zQMcjkpxR=+8MU_VFJFd8UeAl;mTNJLnPHQ&a^qqhqNnDFc&9UMZ5yCG$Kj*?xSSlK%PSY&K zJ5E(881~NXnf7otg#s5a4!$>ZYjXs&uZ| z$C>I=q7uOB__>2HU=T@F%)J+Cj28I@Yb9esyAPMyUXHcBK`0R&)IbuqZZs{ZPo;ff zQgnhk3$h$ia30(8vQC!diN_Tolu_ms1!`e`a16CwMqwb8WX2HQ`E(-BQuFV!x^MLX9 zcMH7ETm8(Z8*y)3)4f)WlbA0HZ}w{^Hp4R|`*G6B{yKa(vMlE+ z@LyB`U}gi&quJ%ui}RI#^Y|3LUaYAlZ0|v!+R7*VKGwVph(R9u>TBG4DAFONUW$jB zHYpQEe8m4b-HSv-bGsf#wCU|p(}xX9uug}!?dTrNDN$DUXu4O(*`|a)UlbiJV@}~P ztD+aMy36$;&M7-`IxY`{#oDw)b%jNZY%vo9$f~KKO#uUel)eb} z>+Xzs7`yPPZCrrb1ixt4oGLHpnvCc>0CR}c6MOqi*=U`}F^lB`mGXg0q_@N(md&a0 zcJTGl#IgYo4T3ZM@=*7SS*>mOS0pk(GT6&+1K~E$lQmgNG}S#x=a)SF$wHfr*MS;IS}eJ{s?BAM zZ6dDUkj#B{JIybR1vOq6%B-ta^fC7$wW?b~mxE{-o!#0&ca-*;E-SC4JP?=Ed=*-A z9*5v&sYw`snba46>&80m(UIW$zX|h}Nnc_zk+YU5l(i7x zfw~4hwzB0kQVW*WK4(^zP7&UK9>zlu+_{r~VC?;?jOS`w{DwIzqqVtHkchxyQw^WN z-^FD(=S2}ULHJFNDjv`m@g;>3)%VA`_Wh>}{Il0^U@0C$w;&%U>G)Qf_Qonmf{LEL zd}*MKH}sT7boW<;x1u8w3ISd>BGv2AF;day=l)V<4>OXByG~-|V0dQT(C|!5sNK35Mnt0;!u_SpEdNe@2zOQwhwMfBuA1?t50hK$eulnxo8*$OrhaXhVc-3*c zaZ0U)#W!XjbHl)r2Dwm=iz)uhIi9MA3_BwcZm{ljS4&T5=tF-#7%Wyh9h=#=yeBAc zEUgVWKz~+#lkW#)5i&_Ng~o7dgKT27Y&#d@+Yg5*+OCQqx$zK4-U4lbOeurG4Idi#xYXUMP)+}q z2h1TaZCYae&2O7nrIImQM|08n!-AI)6v+~sDXF|*4GC6DW12))*TlRXm<+rth)#m= zi}!PA>l@p^iAI(b+^JfA9f_5{%wzDILN+lCq5Nt`pgFkROrFJf&^H8`&~)6$!X=h; zJYqz{Mmh(WwAq=u`zJxiwPS_P~7g1#970- z1i}@enj-e`Fa~APtp77p&M|{X|I|#{fnpaek@UWAi!vF-MLF1&nGic()qC5RVN3Vr#{M`p3W-ce?vY7>4oXJ1PS2Zd+-_1_9*sm* zz{gsF$+|G@D<@YC?Mw)PolY{5sK)%BO?#nFdbph}KE%30%x2*+l33Fq6sij0X83ll z7vIgrcECa>>9Fe@KF$p+r@I^`sNg3+FM?x4`qPBhjs3=caMNPe4R5fa{@0KUgiAZT zRuYtnj9VN&t*Nz&JrP*t6fl$XVuGi!S5s^k^WGCwtYhKtH@S5ArxK}Dl$LXTv{^>~ zzxxNe$>#z$y_%tuWGHG4!F9O5g_k*$%ozjY(BzIC&IY)!oKFEI;|q9O25PH6c`OaS z!Ic4S_77^OU>980hk0cy65dOI`IzJ`6;HTeai8Eeckp$+{y#EH=@ z8$eF>Qx65Myx^~-yJa#BZ2`#kF=_j5+jXeP7QiNfos*5sj?)N)@Q6kLQ6WV)(#3S& z^X10K+Z|s4cKE&+3GWcHP;Nz?f5yFmZn8;Jsa>>FJg4W-o+cCBdR~RE_zLE zaJe0favo<{2?SN+^SQmKQUNPozuEUHq&LzETU{%YU2VGM+#Bu(?@1^hbn`rQWxeJ( z`CP~2zuwY+_&rr&_C*ef)pwN9=>6u5F7Y=5iR_9cop~AN>`~+Td5l8j{HvGP$q2yt4NlTO>S!L z>j+G^aZa108qzUFAw3PiT7lc2NPG|xl0CLnwm&Dqch!@@Hue{HARZ5C)4c;tm^G9I z8}wE8Rx3AO#=O-a){)gWO6xlLChZubgscx+3b~=_|HWGVi@`a};aGLIZ2T4Kz2wz> z(@pE%SYD1}IM5>o{=WZ^m(J(l3`$lfg17ho`#wj;x>Ej&!P^UQeVB-(Jp(;8GqmR4`I;eW|yDe3|Po_ zApI7rDPiR}?JBwi-j2CP>}U{n9FFrs!1U(< zHg`#s8QTtXCA7Ruh#0AFNf-qDN^+p6)1!G-S+LKpBXg((L3QC66cZyPtNA~a4a_hG@ajF1! zsRk9a&RG!jCmFTnlcXqhJO9-FhS&do=|BSC;0=s#n^59p;923bn|&^IFGo3g_^!C_>H z%fUADN+b=7)ZW`^JT(13h8i#Frg?ihhvbHV_F(3wZMdP^cn%LdIJ4Si_$6`(6bXG| zF8+}GCfcMgI3$Aqg#646lXY2#e44ht9;GnfJFn8^5dzM&OOo9GMr9AF%jha9&zQ)H zzi>e^BN^WNDB%WJ2^$3h5jfa{F0kXgY4Xrn-eh*|!MD9It-B^sd?cPyLEUYhI40L5 z@@ui;jC_CT56TWZX|rP{*%SRU^Q6BQ zrET@(7Wt0KG#w%$uU(55Qh`SeI|m547Wr_rBDj=pyht_D=Xt9x!)+`Wo|~pVX<<# zt+`VfIcR9-mJY$XPKDvuMy(%^JG=vNY6{Bw=k`5gT~^FQPHJyw{_+q8Vw`Un1GPo> z{Ae{u>z|_0Ulxpb_Ljf&_?TrDRJiz{XFo78GKYKOnNtY$(@#1;?q$)cfLw{Yn`qrJ ztXuKBB!P%-&bl23woo2PmT$J&Kel}rEymdpy=yhZGm?K^A+kdoBskFI%0Ld#+->nM z7}g-{hYRYURSVSong?s)AOMAepqDB8*@{#|;_77IjhM;R=PEh7Pb-v(QJ^MY$RR|c z(bV9cpg!6Ns_aYMFZ%IbKp1DfBH0G!6-sq0e8 z`kZh1te|5+pN;=QP}vK=fD;78i{?=)R%3yh1N9Y=u4$2@)RUQE{hPGHcdzj4O$=Xf z5-!okxU4eD#R;v8Q1f7+!*Cmt6N)a4f5ANGu-d-C&bda^Ze)V9(p5|XXm+@wCi3F% zn+NnD8`ab0p{M?8IbF-e;63m3=_uv*dHK&DFECL)AIFbl%#g6oD~y}wrzv%$Q{*pj zyyqg582lu-+@jJG49kh!mrUj%7X8IQTgPB)H>p_0g?7C{t8P58D%^P{naG+&GbC#ZO5e7o$+mc)oMpye#3&Eh( z^fP>0o|Fq3Ltp*M?n9B=_|pq4+T_4sPR{{O$qk9W>BF0!Cpilc5OzvqyBS4Am8WT~ zoK9IKK1rE~{d4>~0YEi;cz3pJbe9#Xl^@qL*X83uW%rgEa2qpu4^E6^IwBT*`M zRN;SL;mNdQJC^uo-{{B)f>L>KYwhUs@KLc3c9|ttto?^O=JG-hz5U$DFIUW8tuD8U z23zmgi}}bNe+z4HXb#@Xl?dX3ex6HgJ)kbwpQS|b1gLi-PV#mK?I)XM3IwFt&uTc!~6%hDAGtwu}+dP4q^$Rkgp6 zxKtIInIZY3q*9i9@#%MOX{@y+VDZcvqC?2ghg4_@Qk~`iEckP-L(O4iXS@N6{4lh9 zQtH2&%%gt|D#>?Y)A`4XtJ1fL5tSn2DqLOL59HXn#}hManr>jXf~KnM2hE5yw?_cHg7*+($x!xJ;2Pt#=R&HYB8EZ))snKU(n*OC0gqcrf;0 zF+379z1ov0{F)p66$Q*&LS$$|V;2u5qdbK8S&EIsRax{DE8ROfnKKD-`>zIc&Iz^=23U2JSC`hrlh-Ce5K-?^`E6?SdiVye*MP4Yw#u zq(vC=uR#u(gz~E?KCwDE3Dd=j=aoOx1+f}iYaVYjw`4xcqAE|5d)tW;fziFq*9Bl@ z>9@Lj-sryA&s3{Rc@C0w2HyX!h9|`(S%Eo$;;kMSp^=rM&(&3AV#|L#t$&XI{w6h* zcgn3~Zbt{Ede@55L2C@ofIuu7TE`nym z7^J2xKF|g(=ME&NnU*Ivl-oh|GSqYzsgTfzY^Wvh`d-sWNAJtg?j}#( zS@*`#2CZarWL@jl$_iTaz>EV*nuOig5mTEJ%XS(xb3W}6F>`)vG|>-vg$f3XRFxn0wmT?2pTDM0G*>y}N+ z)}&|jq9(G17?UizB~Q$2>it|?C;a6{yt8~>n(uV-K($RlhrL{LJ|aF3k%mJ}=0l3$ z=GmUHhTqP_m`6Unt?HbBEURujMv2>2NT5G4d2G{@epa9RarJ$2Epjy6HSQD-L!S(9 z@PQhu9y*%03U3^J5;EuHVmlQ?g^pun)eTx6F)InVTL$tjwm} zWuKx?WJ5pDO<-T1LxM_9YPsk43M0saiX$j0|6q6(YHW7}W1M+G=8(pq5-SbSGXx?k zZVPiX2+tw3ML>HF#IpkT9=H1RgAaio^BMVsqi}km@gbN_M5Ui(`o>22kAsKRM}Mwvh1b{9 zR>Yao{&(LJK~wmDONJtoI6Fn47fezSMvM@<5EraW|CT22e2_m#jOQH!ass8``N*S1 z^)6U;}s_~yL z)(CX{OeZ`(5hh;ijb_ZbNp;2@uc0zGxXRx!k7TDnEwys^&Bm$CyKcmJ+VeL`o*;u( zc+z9qNEo7IS$>UlY2q7---~Xj{!mfhOGxo`0dL?v<*wZX|3rIJ-s%%}`}g%PbxAX({n#P=uF zy8Kn%wGk_bD9ZTgD5dhmCPA>G2p%sC@Y1hK(lH86K2fy89;y;&Tx-fhza8bq{RwMkHlgCoW)?@=$^{68wSSYk6of3ZBT41nS(+n| z%VcJm#!T<}oMNg|yQZx7b|-rUZ@97p|!~GQ$GyRswvWAnzXJV>$MbM~ z=3}r~LqnO65Viz?j zGH4FD^lEPAQN{>0L&U^HZ^wmd44EU1EKb1g!oIsS=SZAq-jL@Mk64y~fvk`h)AGBm z7@%xr>Dh_*{<2nSRGWgnG3vk})BSkgQ}TF3Hftltm)E;;u8vJMG6M!*hFs$_5TYe> z$8K$1!-k>t|G0XmAkn&@NwjVEY1_7K+qP}nwr%UQZQHhO>-6-0=T1y~^RSHv%0!S@sLCi?Kj!`1U8GhjgSUZb^%FxUVe$=3Sg>dIcYlD_eN&_V;D||Dd>uMgJy_`qJnHGmgT5{<;Te}nyR@^owR{p|n*0GRwup#KUrOl*I3 zB^I`3znK$~FmIPdk1(?PP9=K|>@KAMtf(wB+j z1P$eCsu_W*XyG0NbO6I(K7bWyCI<$S9QZJ{A~9lIWN|uZ-;e``MOg6gxaR33qO?~E z9&xmpR#@ZAhjInnnm32nbk*O8R~0kf_kub-sjg}dK+yyiK?}{QL)w@t7RwY9hYH0y zHSE?Fdvv!y&)sslEwKqMk+CkGT@Xq3vA-O9vSpaWm%x9u^WJL+_Its=JnAS12KUW0 z3MaszfpF8r(M10HWQ#s`T8c1WW|W}~e*ks&6T)~)_upVY2wnFT{{}ngH`r+Z8EcqY zSerO`I60fx{D#{`@&A<9oed!+d?r`iy~V!NgLU!a|;?(E`^%ZWt>Xd;ImT4`i99kRa-GL{F-N_Pi@!*=|hLD#FdWr zNS?0QK$u^KavZF$w)oVG`$CVAZQ#aeLV$ReECo0G@K_$ykyaJ(f2T#I` z**l1hr17k4N%Ey($5HW`BUgsA6bFZH&Osb2Sgy&7Exp#Z8u%=)*Ss~hHt0`=7Lo4m zUz7t6J%%2=cBT+bfXPQCm6&dzgeEpF&@w!1t{OvIJHGEfPVR3W=T4KnbtAq$`(Slb zUDzFkJJHG`-|+tpYz6ZGVWd8q&!%9>Y><(FBe#ce#p6o+_ ziol)Mp#?&Ck+oW%e>;UXw{ar}6tFo~2&Hm~*{0!Mcf}N2tXe5pt-%7v7)5-)#1p7< zY6xa0S({`}ILbTfY5mnbsy6W--*Sn!eM!7amMJA9IakX`lj1_G0~ zS2&zSZ_&u@cmh(q7ciR$fXpelLsy9eYf{8mMrA!iXfbzU`x=f;UOo=P^w4jIVF`OK z3Fy$jTKT+JYj*q|ZC&Gh|7g0;&ZqJ=SMeDmo{&NAIXOE!gK?2%hQ13URH>dfhjlCq zuxK)VLCj_fHt30vMAeX;v4qFSr?XdETV2gy+g0lb;T_I^{nVL6tXD@vdE|OlOp26! zJpnV$0b=6ipB5v^F6Hb&X7_8N1OPFD{Hk=y>nA{@jUiCuyY`9@!p!GH{t{=->TGzAZRdl=|YFBjiBY+VF^y#5zW_cVnrnH4PiGRPhf;`z`$v*=~QX_`1%Irk+1aH5z>xIX@Oax*UqGMjh-HqiM9FU>kPe+;cVIQZY39tc_!^PXCf< z(R)$ii9j|#>y%^`y5anFK&_tH$==zpk#kTuan6UsXE1K8ver{=_r^mVp%PG8f438C zuxbA;#v=)zHu#ylEY5umDMips+JyUauDrlDg9EsNMV2`?OGU9P`*?Ia76>Tddl)m@yo?>8Pfl{U?ujzbk%V zPHk|#fNTH3-T1)$9xX4NoI|C9D~(ekn)4?S-+ds1gF;fWN?H$k1HH-hl&Yi{6Ef9C zC+|@g8xiN6GzGT60G8Hx8)1=xr?%DYHSTl2gC+X%CKbvCm^LHQ{<-j-38h52JT6m8 z#4>R!KZY4|94e){vb+$8TB8jUMc7|krW?`8&@(oj-2D>ac6MWKLnIWRNuN9t#qGXs z3qEOJ@O-NfbVE`giFll#yPb7Np8ZnEznvOFuJH_$Ndbu6FeczkZ0_4<4=!vq6T-2W zlENaSE3i2}vPIW>f;)8G`ouR4=b4j<8&w&rI~gLeeeAEFHL-j~)F2{ZH19lgec0{Czy z^APnl)l?pkqacfFteI$@mGdCW^Sy5ici5&E$pQ$+RrmKOLYa@9?o0mQ?&eWg8YR-q z@Lc^$=rAUmg?y;=-oU%ATeB`UGueCFW<8#x$!Su2DAK_1o@m<|)KAgL?f~DqpNF7k zx{`SdHCeVZxXO8c3VPkb76A|q;T9Nf@*#YLNJ%mm!dd5Z2iqIf?;nDp&Cls?Tlnml z_6`Af`m6Z-rX`<6B7AJq;H(fbS8j#vIxszD)x=3O_EDpg&NkJ~?QdJ&gTh(7r3{%} zoz&^F6uh*~YtPTqpNgOE4eJ{`BWSss7)O~hLtjixmasx&(pe!7tI@uJ*t2qc()a$P zN{t08frG)F1}norkWY1;D-}QuycHy{vmkHdgHt&R;zZcKXNi`sCk_Wf#GP8>NO@b= zkcdNha`CYWNyz-`W9BvarBj@N*hnKj#OoHpKa1GY={(%CjH5=*A>SgIGNt3O zmC*cqTO~L>bg7@$nEmcGy+PuZ(!)$jiu&jU=T`>JpFrZM^tNf6-<~f;wFY{fly@4~ z-Y?dpzI74*{wY>HGic%+s%B^tI3(vTw-wVlUZ4iyus*`o+)4ISjSlpey9MWD#-Fu@ zwA#~f^}Y7&^JM+u;O|t;H_Z|2VxSrXP=se!D3UKh`IO?pk;3oLvCwSCQaL~{g zCb#_u3b-d;4t)o-LWl_`ymTd(XS_D`Fpj@3VVAd(?#RVR3EZ(r4)4_s#FHCYr-$eb zu4oq{AiBJx>zb{SO4wfDye?t*i0{83C$7A95dj7OAotr<{kO5Qb8$8{a5nj$LDsUm z%`eI!{JYWXuYe$gZ+Qtc01iA6iZ8aBdjgeNDVzifNSI+jj!>4Eh*fy%y^ZxOIdTTu z@J;6>MjGeB&Fl2KISWzch&mLpkb-rX64oWR!RTe6q@o#Gel=E%E87Z0;b7i%6CFR{ zQEF`!++od|5C(0XJXy4!GH%GnjJ0#G4-&0b=aQp=B#PjndE zXm7*4vWYLpcC!nvZs3lBC6+8T9ofn2=JfdfSE!X!^@nk>+PBfeX=S>=W~=bQ^`kCo zvGx9b{{C_C?da+{`dm?xu`rq9%$=l(#skc8xq`grtSN{Y#D(%HdOI*CgxOph%;NXz zK})EQzys}3M5rO?h=<%!uPHT`G=6mO^o616{6)yi(ZL;)*WG0tM!oh)zdL#Ab$-0_ z7}T8x?uF(D-j<3Dig{`kCt^O*js<?cT)LT^+uVnv0?4>!IFU?Pf%5?Bb2)F$2nHNj zWjPXa*AYC);%A1r9X||0lnBnH<+lJH%9afl0Me@ z>Iwp`D`{`VQ-E%t-Mg&udg7xkpA)_`Vd(T;-m8*EDP8NH$9ihNgtm2BsGp@(GHZ}N z9W;DNfc{z4#p2Pd)Ykn4N6BXhLq_)GmcZbBs^a|9v$BB@GO#$=x3m09187LIR zIEJD*6^^^u+Bt6_OI4aWUD=2i4oj{8d^;!|AR&G5{&jPigjq*h~$R;*wbe*HV{mHCkF^oob)`E z*4tA}Q!5+usll7F{*DI{LyNPj9WIE#$V${TUZ?SiKu^b45gMc8c&7yD%XGtneIrQ5 zb^7LV$0>O@$_xOizmVF%TVhig{Mv>H%*4q2Vh6?XvRLQ)5h&ts zbf#w%OU0TvMSlH&=; zl-Zv8hhRmYAd2Cv2(ihko6n0tB^GItRcbO>*}mLY9oQGA)RD|-HRV1OHiXNZ*)QSG zAk=5t;`xV0^@;o}g8K~f&!*@3FHt^EBnq6e89b&n0;|hE1BV`~-E%@NJ525qxea`4aaXImceA&AoqR@;m~HD!n?dIt zdbc|-dv-$eP6#KnIu>_^FhZ*}?WsO?9;nd=?U5Er@ir(*b3V4wY}=MZQ0@n7ZAD^jOgg*IR~Mbr~W9L*tGO zIJtixmj5Q3^=R|jSethb35X8oonU}<6Q0{(!?mqGLrPtLeQvZh)&llEHo9u3iO?=q zLVq3OVA6f7LwzKDIGF+4O|tcJ_??5wo1^^LvBSLXOCh?Ue#C#?+>vEzABT*NQ94?gJJQbT0Mc+897_OC**(4My1w!PV&L zxsoe<>mno4^|FtQdw zwVaEPCHn#TZ|xGM1T(7soe$mpemMV`6aDVVbFp`}v$J;6`#)riOicg(xXuXQen*NV z5X*l6mxu`nRK$By15buuJuUU*kdJm9BnUB}4-$l*^-Jk5B1M3ol2#s{^>{4HO74oiP95W(=Nk0s;>3X8Rd>5R8$YQkH|}h zh!*ep?=g=L;!_Oa003lY007|sb4(YD-{L&2sbz;XisE}(hb~V))LfBr-6Wu|EPyjA z5!~Ub&v7S&O&!`P$RC zYx5W*DO*Y{%^`HraS5 z)4z}BSG%2E=Q=DZDdb&Rm+pO5rU&@?Rzld|!-*996Q}~}R$}{3&Q9dYUuWF4@BYrF z%I8Ydkz(i_+OODEC)b(>P3v&;h*>h$S`m$ij$^Nvp3qXMXEzVJW$%H1*Liuhy?z

6ZZo+(G3Xq1EV1m~jbnI~)EIa|hH21> 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 N1J{QMA>cg?__TU0 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

{j<9lqLq8)K{# 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 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/progress-1.2-py2.py3-none-any.whl b/venv/share/python-wheels/progress-1.2-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..892afeb93658fc47760188e742fd77964933e9b4 GIT binary patch literal 9605 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 X>pLw~yt@nl0J?kF?hY6b|JUw6(1V)q literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/requests-2.9.1-py2.py3-none-any.whl b/venv/share/python-wheels/requests-2.9.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..091c1ec2f4ed507960394899bff40b485d68b4fe GIT binary patch literal 72469 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 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl b/venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..b5992ae03b30fc8b9aa7af226f7a154cd00b87e8 GIT binary patch literal 9536 zcmai)1#BHlwrGzrX2xTtn3*wVW@g9C%nUIzQ_RfFOffSvGc(5V&z*Ur(Tz0sSE<$N zZfU7XwWVIWcF9YDfujKc04RW6A*;l-AvvrkGyqVG1prX|e(PZD;(0r#^C%2hGponSxOjt$ z7e1Dxn?EU450-zMyW!w)FMg+(3v-UB)mmjzvpLt)kg&$^&E__*JJ2BWmbW3i=7gW6 zRdy>)>{a?SW!xn%G1DTGchR!B&V6^HbShJs;DMUFc+GkL_9A2^l4-^~`R07l>-!j6 z{`h@bSDa+hGL1GVl?+wkR2sY5p;lg$QzQCxaAA>p#(EH@W!5^_;J^uL6v`U1`&L2- zx#GI5OiIT}*l&RFy|N}K)?HRhBRI93?57o8<*)T;!u@r&XG_9B9(F7^o0=2|$_^Vl zkIy%sMjtiB6%ubqVZ5$&R$QD>%v5sA zJ82UOX1c5A7`idKI5m2?0nP#duGY$Di5;uPg*;EW?!9Q=fvbB~wV+|^Q|rRzng&MM zB+W@FXE&!&sprVNr0i+m2I!E4+*H}sVSA^b8ky^=Io=6aDONH(CW*V01kOOagIk7F zX+nS`*ogJ3uZ6E0TC0ODTMvZ+`U+&k`y}usKNUd>C99tNhzE4`a2M3Tfj&}*Nx0W0 zXT*Ke>7V4ay>k#T_xePrxC!=(!nabyMHprk^P;Cd6xKE?&O*uu!E*TSSo%y@%ds>U z)TLyuRD6?So1p}m(fM5$x%ZRWQ}iONAs&Pi&NY#|O;^!_E%tB(Z$@2${8L#&;{q0@Ep;K{lg!P0*%TQJHsQF}wSbOSNXkzWd{U*K;FSGDnYU^dOL8l7U71C_m$et+YKgQ9a+IGvdA)ijKwG?tSb<0~{u+`gL;L|8Y0)nX21DQIk-VZF^;&)Z5jckbE10 zH?KtcyO(wwbI`xu)XgLE7ESMql!tu9c9>=(N^P2${Fcf@?G`@+4h#h+glR-!Q~Ye| zS!E=lYoy*N5EewzM4dIp<^fV+?I}56G}adHH#9HKiyu&&@fr{@!fYj07NNYRZ2x5* zcEf(E7XMhmAORXz6@-|!=*>mz1zxJa=G&q~o($mI2SeCHdE7h{W5m8pZIN+oK#nFp9h6sX z7>Q_s-n+6tX&l)Cg2$>WbJOw4o@EYj%I`rGZ}94aT3M^|<{T5s8(Hzn)}8Crequ!_ z{EM(jBGCF3Rc9&xxJmQz8<0{0+$nn7q$%B!WbZ5HnQwuD^cNy2x75Pa*P^0YX?x@x zj5saj=yD~P!k{cQj|oz}kyu*fAI*ymD%-lgZ!*N2bSf@m+NPFypRRDOm7kR2<+~$e z+yR3$vb@Nnl8ZU!es!}7z61Ls{qfUZW#avq)&YVX`?GCYH=e@89`I3V=69ZwU_(<} z9hQe$BS{@(1bw9Bb%iXjKRQ{hz2CWXZ99Bucb!8x28b@ysJyPz5yZT<$)&Fbj{H3X z3I}0wtQQ>E`#V9(m0*Q4q0QI2PsY^+%;n%V;YDa(G;1rJ`%wpdfn0t(GPh=~L6Uga z^hn4@4#GaU(@<)ov?XVKt8HodezZgi8Tes{D4A)+sFVbE8BtGVG{;Xbw(M7Yx;n*- zsBtWdpDrsmWR;%_s!dBB?A1^}P3h*2r4Z1J1R3{bk3A+3j4`9UUAuR)J8%imW|PCkZJY0n@C-sEJWC z+r09i6U_{M!mM%Fp=08jkQgy23;7Vw0t;K@F5?T6Lvo>`OJ2m3HZ9lk8T#Kq{g zY%R$ziUK;$I%S5YOiXc!nR#Zl_0AuP*fE!ecVNF>`euv}>bmFC5RR8AN4+3K28JJl zWGeHPN1~U#OS;2lk0dmZuU!V7D)&2K7%t^*W`xf-?7Tl60~GE-Yp4fv2sW7WoMxFv zuWVo~O)cS2ynMluinWqr#D?bHdxP>CR4(EmlH>sx^Y}`kuQr^U>ERl;dq}>r!I2Q7 zs`9c?nT`_-IAL3-xaL@;tLy`71+H7s4Jv-hSq;71pOc<;596_QM1zZiwT#FLi%qdg zo#lhCEMMvp3}!6a8g0TjMcb?-MAEp>^iJED9jp6j&k0~G(X*Yx<;3c}ySnD1Ng+oP zIKo2k@R)(}F`D0O(!%g{%wY*}_b~N$G=X^c1YS73YFTDnrv;mK0GC9UC`M)cHo~pz z4?-l{E!P#et`BpkBgrW3?2hSdapGTV>8JSGBfR8w zO=ib_`6fEACK@t^f0_xRnO9doxKK=~FY#oLjM>dUE~A4nPcgJ#p>s2ZS*YHJ`+m5o zX5;0?G3TQzw>ys(;$m|vz0xncDRFT$Xwa=4?)tOJeO@hS{zcuZ>-k}KFD2r*Q}(w_ zJQT#AXxG|wmmTyik4K3VDH;~CyF5%l6ml;FwMnTQ6e$-jF=3*nZKj^Eye3M0 zVQo^+eEys=c~Yt>f7NkcHm>|x_Efr@NyUH%!*-!^R0bl(s-iQtt10%UZgM>^I;XJs zfM&_2tG22(RM)qID?}%WwVA_UeZ|eV$cL!XBE`n)!)RiBG>>p@G3K=)@NJZwrqDM3 z`5?iy!(%r(-|c=MPfTv}#=vl>vD8VDp2Dw5+2?I-O802h@*q#@!Lzc`;k|kFrPl6M z_WZ+(CsOanw{H}u@;)7Bs6T3_A$om-d~FXZUEY<41}E_g__dSjePt?W=3eZH@a7}I z`3&hVq+f<4!k4D zdx)PI=VrzpX!b?$>$OCFL#M(zgUCrp3&@tPq*T>xxH@rNzPY;2>@u;SVoKncX2nUKFH~twPSCy}h;)a} zX0+ZPx=*2d|CK`?{0;1;9;-@qW=ImAKEs!%adG**b7^tC=UsNO^c1>|mm~K4JBiUs zz3EhNKarc;RZc>_1H;dv-+H^utwEv4I_%kmIbKSGwrc%bZ;L`Bi4@V^`0@L?&vu!_oyX%g$vNC&R$N$Ywh(TjjYb(6?S zG)v=;dg{F4OHtoj$@*pO;3=wmfOsYr7VvZ?WW>$1>61q{bGGKNbU`3dUV(T+XICiV z#R@lt{1ij%32Wn@6AY6Bfp`C?RPIPo&o~>A-YP4oioYIPSX4yRUz(#Pbh~ocGvlR> zD)g%Y-2@vQBM4x_GdPx>ry&)(*L@+t)%6pRANHyO*WLkt_*`P>haXCgilDnk16r7r zRxlCjm^m5bbeMfaD5bG*ILJl1tqlf3jw@c;_eoa=rYHSl> zTUGNfp;gPYsCkR9_!+?8=i>J{VcW;_D<5YdTykaO`B%Fs!F|YG3_?_4j;Ud59RyB2 zxnj8nMx|D5e8f-BR;;!h;`sW@b^zxv&fyxwLFAsBMOKW%S5aiiTr1;S+_b1v88UQ= zft8$MkfX}9$;OBsCX0j3Pm_D&q}U_MTczi24f;ggSk|l;W3R1i*B&AiR_9>ir8k32 zvsmV0MEyQ<$yxC^6=T)oR&w%O=d#mu4t5>}d!-#~CUSQ!sc!ZCO;WmX?8;vXQqS+N z@!4UwAJgW|_*o!K=&Uh7p3u}jp{L70SoY4Vna&1cp%AN%2w*9-J%R!~Q(fet8Q zmpmn=R%K?Q{h!btmPMgoFY$(cQ5U*$&D-gA-EI9#XmR_ed1EM9+D+&3d!fO4;ktC3YQyU z1+4s*#gTBF4vLFW1FhJOntmz4T~&&urRH(Z1&uQdmHwUS1OxvIgGmseYKWSoTWy&G zrVu&}%u&sRrP8h%8n*2Ma*Jm9f>%Qf2rz1F3efIQTu*#q+NqDPNn$74-zi}EKul2Y zhLMOusu$lxv(SX0geu$F3FyI0=P(d{U64T+M@dt<$SKxgH!Q;QMVUbX47=}x6uY{7Zs%*D+c${&42Lc zr}}sS?--;Qot;Gt({_pZX^ERlaMjAuBw=~!@9`46h`hPP(6&@sAR<^zmj~G!%q%`w2TK&+yNr>5t$Bd zhHinP%vhR@OOI`#06BbSe(CUPNYL%WzaV|qh?_L#R9e@XTRfWTe;B+wBGz^d^~Rm` zD(w4IZSLFi$*1>T*6XsBJk_!Su!;fi%Diq_!_~+?pBCG+@qIB8BGF}F`$B#oz8Y;! zbAd zHGkFzYUEs!Y)WpjRO$^JMXlZizxT>OrB)K{FD?NCHXD;RqUH>OHc4xpP<6g!4;3ho1KHb zkBf&hp5JhaxH<;TRe%%7b?fLQpAHzNfjz)+-2YjNU9~ zK3(!R-$$`8K6@F#iK$|WT^djsLrNq%Ax?Y<4r{6Kx{qS*_r6h)#AiF;iStcxRp{Ki zF{SgIV#zRrWp|*Il<6Ly!`qwXsrqMhtb7K1w>Z5YfJ+dTQ@*EUqO1qx%Z0VR zu}qLaz?E&ztzF@*!iQN4cZ+;tQyE@e4hl?9$5waPSZbepffdg}SlIVxVu1Q)jtX-%XvQdt8MJm+R< zwcrmW+|#toGvBDKK>`hb64PjpD0;qs+7i7^B2SN|s!GQ7V=c7SNmj;RCDgn7f)vVr zWX<;N>I3}m3VXK!mS_e70C0c;00jS}u!nGiu=ZxiJDg2~77BOP2#hSw4sHGMZbLkXjrUpTIZdkS3Fs^x<^ zW9e+%xc60??KcS3^m=7+(MWAD-aeukMIhI|M17HTQB*(Zt3?OZnx#I(*!y(8x-Y-- zgW|1}18%M1U4$}%G%nZ4Nm=KGE+8@CZ|_H?0Glk}UQ}Kf_Gd{nv}oF3LU;zxd6f+4 z?@W*e#(Gj;I6uzFTeW(_#ErWV?dHIVDYO&XLP&}w>+1MF4PKH=bK2;~rv~qI%yzC{ z^X-6PYPet?ENbuypK(CTLW&3^=eR6Z=BljO6xzj;($(nnipydlpVoGFx+Scx9f@?^ zYN~9N8x^sMpX~)gFI&CuW8+_prTVy|j(W%{vBC13g(zuBi%{3RJdt1fplIx89lcAx z=g5a@+}Ptj)}8UrE$ge}ZbxRA@nB~>{(6M}`y!RE1)%EvUL2d>7ydsjlA5@Ph&1e! zxXi>1yc8|fmfV<&wheJ6c73rAaJl!9XB`3}J9=Du+8&@9^xa zY?N$U)#Evq<433FMj?mX!*>^37n8?51VwK2{0;RUisIdr3{Ozoo=^=-8aH?YE_C)9 z2u$Fh7xJxuLd0m8=fRk261M%Bl&Rs5?5j#h0>?*tPR#nE-Wr)e$}Cn%HNs?lA?_eT zND4JthQyN7)z@3p8v{9&>kqMOOGPhkbT?<Oy8Hh9uDpFLLd(HhXj4zmyQQPOqbs2J zYUJhjhQU>e8i7fmf1@9wCl!PRz`&t_RH|Xc2HR0s9F`=WMq^wF|C_~&kO%>mI|Y8) zumFi7Q|%!NFO@(KxIRS{XfXW)50`I$X4bT`Ib zM@W8WyXk$mFp@2|1!B{uXjhK1woQ?{D=;}cqi}1?%*#!9ohJSH_39G_W&l-6h#+aPQ9An}g!AtN(15N6dvw#zy{mRH&GXq$>|NF_ls_X2FW9>3N+IS*_(iNRZb zO9nF7np|DfIhuYr2GplJxyF_2Z^0W-nxZIR(GZm5jR~5RP<}|1q~n~-6SMJT3P4;| z8}m*BU;b0c{mRK+7UA3gGz|4K?a7DqWD$1D2^NRuGHj}32{z^1kHSox{OUrg0)Y$x zC}%I}eHO0V0fhm_syvEV8YxpSfiPu}))p# z=&@s$NEmEUN?(H!Sr{dsJk}{%fi;EK21iCToL+{t9v)=UPIKVUl_+LE{xEXe|&sTy(_zZm_p3!Qq$}$gv%p=5vzgaUvAq z@|;XDEDL=W6ejuM?!h(|Hu!eR-IG8(65w7zzRVSxV@MG86UkXC{#VRYNa=iyZwQL) zmqI52m^oaymTpjsGM9n{a^5=?Y4+0|rpNvWa|6gr%)I*yq^o14-p3hD5oU-}kG(@n z+0Dbv2V8k*M(dqxCJ~J{7&%^< zJp^xN~y!e7* zMWHijru64#^>NmwE>$z6?^xtpbb~HCHLW&1a^BOp$m03ur+1xg7G%Fn(|7UEBt&W# z~xuH?jwtY1N2msKo5xj8W= zcnlr2O6>Kn*dBiAk>TzP@w`7X-}0%Rh;?1RW}0ISmLR|A0wQt?9Lv{u%~hF1KQNlH zcw$NYa*Vd9GQNoC^$_$H3SbPV(%>TQRfqPJk}Xnl^_=ngkUYG!nI2^(;MiJ)oR0do zc$m}y)zb3|O>>{IBy$kQ{4yG8$mr#h;lU>w$7$LX{xWe;>>@S?4>T(uK1UV^=c&%yjKWn8sq?KFE9uMgkI_BthY zUG9qP!w%A^IN5e+wTaOLJASjscTJyq9bz4W+#6g>w9Px%& zZ4cZ=OyX+p$sv>ZIj7dfI21 zsgmCK>=?MtMNAG?EpxAeb6;HE_CdAVg7WMcT!6w6f^;7C1HB4b3u5*4q6?g5JVazf z;&XdR{!?TRZ2e*;5baur-V*mS{~3=f3VWf|p?5j!?B*ucS{^Gd^I3Mn_2S&a+K0NH z*}{s&3Xjzd-5kDahpjxQ_0jIgp$M(uov>p;le#k#dvP(T4$q1A{wW^wse~5Pu?r)lxkz!3jqMXND%PXU(aV zZRAIhCk+in9dOB9_Bv&2dvcB={(_a40s%z>``?FQe*50PT7myxxBsZ$|L|Z(FpR7OA zu>Z0K{)zRsr0h@TpEmzr=FIN{OaF7Se{=gknSUz%f0=~X|7HFbe0eDd$iF^*`rQeB M?@hpx{Ppzz0K}V>>i_@% literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/setuptools-20.7.0-py2.py3-none-any.whl b/venv/share/python-wheels/setuptools-20.7.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..10c5ea9ad79038b1032d2eb67a0f63e4565af27e GIT binary patch literal 120358 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

Gbpa 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 P1;8URS3h7W0Q&y`>CXA7 literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/six-1.10.0-py2.py3-none-any.whl b/venv/share/python-wheels/six-1.10.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..7895a7412c5c4d570cdd337eaf9242c32c8ed89c GIT binary patch literal 10342 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

x1$z^nR|>LPnhSa})|&1%1OVWnSt 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 GKK*}z>|AdE literal 0 HcmV?d00001 diff --git a/venv/share/python-wheels/wheel-0.29.0-py2.py3-none-any.whl b/venv/share/python-wheels/wheel-0.29.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..71f3a5c8650e5d472f95537cb1b6f696276d6436 GIT binary patch literal 65953 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 LY;NZK=YRhX5%mB{ literal 0 HcmV?d00001 From 3ae5b654466f4f04f9d64cb10f9cf7b5fa219ed8 Mon Sep 17 00:00:00 2001 From: Yosef Levy Date: Tue, 4 Jul 2017 21:45:56 +0300 Subject: [PATCH 2/2] just comment to activate travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f6c4d5a..673c4f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python python: - "3.5" -#install: +#install: # - pip install . script: pytest